summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2024-08-22 12:55:49 -0600
committerRusty Wagner <rusty.wagner@gmail.com>2024-10-21 13:56:55 -0400
commitd8e3001e535fad178c621ff07418f81f25123dc4 (patch)
tree54c03cef2f0bdfd9a3b6df4334c81becb63e4993
parent0e281a30d73c0f31ef9442fef0346779164231ad (diff)
Allow multiple high level representations for display, add Pseudo Rust and a Pseudo Python example plugin
-rw-r--r--architecture.cpp142
-rw-r--r--binaryninjaapi.h541
-rw-r--r--binaryninjacore.h267
-rw-r--r--binaryview.cpp38
-rw-r--r--databuffer.cpp4
-rw-r--r--datarenderer.cpp14
-rw-r--r--function.cpp27
-rw-r--r--highlevelil.cpp341
-rw-r--r--lang/c/CMakeLists.txt45
-rw-r--r--lang/c/pseudoc.cpp2852
-rw-r--r--lang/c/pseudoc.h66
-rw-r--r--lang/rust/CMakeLists.txt45
-rw-r--r--lang/rust/pseudorust.cpp2925
-rw-r--r--lang/rust/pseudorust.h79
-rw-r--r--lang/rust/rusttypes.cpp339
-rw-r--r--lang/rust/rusttypes.h32
-rw-r--r--languagerepresentation.cpp484
-rw-r--r--linearviewobject.cpp9
-rw-r--r--python/__init__.py1
-rw-r--r--python/binaryview.py35
-rw-r--r--python/databuffer.py4
-rw-r--r--python/examples/pseudo_python.py1100
-rw-r--r--python/function.py77
-rw-r--r--python/highlevelil.py4
-rw-r--r--python/languagerepresentation.py822
-rw-r--r--python/lineardisassembly.py10
-rw-r--r--python/scriptingprovider.py18
-rw-r--r--python/types.py67
-rw-r--r--python/variable.py10
-rw-r--r--rust/examples/decompile/src/main.rs2
-rw-r--r--rust/src/databuffer.rs4
-rw-r--r--rust/src/function.rs83
-rw-r--r--rust/src/linearview.rs11
-rw-r--r--type.cpp56
-rw-r--r--ui/commands.h10
-rw-r--r--ui/disassemblyview.h16
-rw-r--r--ui/ilchooser.h6
-rw-r--r--ui/linearview.h8
-rw-r--r--ui/util.h2
-rw-r--r--ui/variablelist.h6
-rw-r--r--ui/viewframe.h26
-rw-r--r--ui/xreflist.h16
42 files changed, 10470 insertions, 174 deletions
diff --git a/architecture.cpp b/architecture.cpp
index aa229b4e..a86d7256 100644
--- a/architecture.cpp
+++ b/architecture.cpp
@@ -60,6 +60,17 @@ InstructionTextToken::InstructionTextToken() :
}
+InstructionTextToken::InstructionTextToken(uint8_t confidence, BNInstructionTextTokenType t, const string& txt) :
+ type(t), text(txt), value(0), width(WidthIsByteCount), size(0), operand(BN_INVALID_OPERAND),
+ context(NoTokenContext), confidence(confidence), address(0), exprIndex(BN_INVALID_EXPR)
+{
+ if (width == WidthIsByteCount)
+ {
+ width = text.size();
+ }
+}
+
+
InstructionTextToken::InstructionTextToken(BNInstructionTextTokenType t, const std::string& txt, uint64_t val, size_t s,
size_t o, uint8_t c, const vector<string>& n, uint64_t w) :
type(t),
@@ -107,7 +118,7 @@ InstructionTextToken InstructionTextToken::WithConfidence(uint8_t conf)
}
-static void ConvertInstructionTextToken(const InstructionTextToken& token, BNInstructionTextToken* result)
+void InstructionTextToken::ConvertInstructionTextToken(const InstructionTextToken& token, BNInstructionTextToken* result)
{
result->type = token.type;
result->text = BNAllocString(token.text.c_str());
@@ -144,15 +155,19 @@ BNInstructionTextToken* InstructionTextToken::CreateInstructionTextTokenList(con
}
+void InstructionTextToken::FreeInstructionTextToken(BNInstructionTextToken* token)
+{
+ BNFreeString(token->text);
+ for (size_t j = 0; j < token->namesCount; j++)
+ BNFreeString(token->typeNames[j]);
+ delete[] token->typeNames;
+}
+
+
void InstructionTextToken::FreeInstructionTextTokenList(BNInstructionTextToken* tokens, size_t count)
{
for (size_t i = 0; i < count; i++)
- {
- BNFreeString(tokens[i].text);
- for (size_t j = 0; j < tokens[i].namesCount; j++)
- BNFreeString(tokens[i].typeNames[j]);
- delete[] tokens[i].typeNames;
- }
+ FreeInstructionTextToken(&tokens[i]);
delete[] tokens;
}
@@ -2285,7 +2300,8 @@ void ArchitectureHook::Register(BNCustomArchitecture* callbacks)
string DisassemblyTextRenderer::GetDisplayStringForInteger(
Ref<BinaryView> binaryView, BNIntegerDisplayType type, uint64_t value, size_t inputWidth, bool isSigned)
{
- char* str = BNGetDisplayStringForInteger(binaryView->GetObject(), type, value, inputWidth, isSigned);
+ char* str = BNGetDisplayStringForInteger(binaryView ? binaryView->GetObject() : nullptr,
+ type, value, inputWidth, isSigned);
string s(str);
BNFreeString(str);
return s;
@@ -2527,6 +2543,24 @@ bool DisassemblyTextRenderer::AddSymbolToken(
}
+BNSymbolDisplayResult DisassemblyTextRenderer::AddSymbolTokenStatic(
+ std::vector<InstructionTextToken>& tokens, uint64_t addr, size_t size, size_t operand,
+ BinaryView* data, size_t maxSymbolWidth, Function* func, uint8_t confidence,
+ BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence, uint64_t instrAddr, uint64_t exprIndex)
+{
+ BNInstructionTextToken* result = nullptr;
+ size_t count = 0;
+ BNSymbolDisplayResult display = BNGetDisassemblyTextRendererSymbolTokensStatic(
+ addr, size, operand, data ? data->GetObject() : nullptr,
+ maxSymbolWidth, func ? func->GetObject() : nullptr, confidence, symbolDisplay, precedence,
+ instrAddr, exprIndex, &result, &count);
+ vector<InstructionTextToken> newTokens =
+ InstructionTextToken::ConvertAndFreeInstructionTextTokenList(result, count);
+ tokens.insert(tokens.end(), newTokens.begin(), newTokens.end());
+ return display;
+}
+
+
void DisassemblyTextRenderer::AddStackVariableReferenceTokens(
vector<InstructionTextToken>& tokens, const StackVariableReference& ref)
{
@@ -2560,7 +2594,7 @@ void DisassemblyTextRenderer::AddIntegerToken(
vector<InstructionTextToken>& tokens, const InstructionTextToken& token, Architecture* arch, uint64_t addr)
{
BNInstructionTextToken inToken;
- ConvertInstructionTextToken(token, &inToken);
+ InstructionTextToken::ConvertInstructionTextToken(token, &inToken);
size_t count = 0;
BNInstructionTextToken* result =
@@ -2606,3 +2640,93 @@ void DisassemblyTextRenderer::WrapComment(DisassemblyTextLine& line, vector<Disa
BNFreeDisassemblyTextLines(result, count);
BNFreeInstructionText(inLine.tokens, inLine.count);
}
+
+
+string DisassemblyTextRenderer::GetStringLiteralPrefix(BNStringType type)
+{
+ char* prefix = BNGetStringLiteralPrefix(type);
+ string result = prefix;
+ BNFreeString(prefix);
+ return result;
+}
+
+
+FunctionViewType::FunctionViewType(BNFunctionGraphType viewType) : type(viewType)
+{
+ if (type == HighLevelLanguageRepresentationFunctionGraph)
+ name = "Pseudo C";
+}
+
+
+FunctionViewType::FunctionViewType(const BNFunctionViewType& viewType) : type(viewType.type)
+{
+ if (type == HighLevelLanguageRepresentationFunctionGraph)
+ {
+ if (viewType.name)
+ name = viewType.name;
+ else
+ name = "Pseudo C";
+ }
+}
+
+
+BNFunctionViewType FunctionViewType::ToAPIObject() const
+{
+ BNFunctionViewType result;
+ result.type = type;
+ result.name = nullptr;
+ if (type == HighLevelLanguageRepresentationFunctionGraph)
+ result.name = name.c_str();
+ return result;
+}
+
+
+BNFunctionGraphType FunctionViewType::GetBackingILType() const
+{
+ if (type == HighLevelLanguageRepresentationFunctionGraph)
+ return HighLevelILFunctionGraph;
+ return type;
+}
+
+
+bool FunctionViewType::IsValidForView(BinaryView* view) const
+{
+ if (type != HighLevelLanguageRepresentationFunctionGraph)
+ return true;
+ Ref<LanguageRepresentationFunctionType> type = LanguageRepresentationFunctionType::GetByName(name);
+ if (!type)
+ return false;
+ return type->IsValid(view);
+}
+
+
+bool FunctionViewType::operator==(const FunctionViewType& other) const
+{
+ if (type != other.type)
+ return false;
+ if (type != HighLevelLanguageRepresentationFunctionGraph)
+ return true;
+ return name == other.name;
+}
+
+
+bool FunctionViewType::operator!=(const FunctionViewType& other) const
+{
+ if (type != other.type)
+ return true;
+ if (type != HighLevelLanguageRepresentationFunctionGraph)
+ return false;
+ return name != other.name;
+}
+
+
+bool FunctionViewType::operator<(const FunctionViewType& other) const
+{
+ if (type < other.type)
+ return true;
+ if (type > other.type)
+ return false;
+ if (type != HighLevelLanguageRepresentationFunctionGraph)
+ return false;
+ return name < other.name;
+}
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 0223757c..01f5ba11 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -2169,16 +2169,17 @@ namespace BinaryNinja {
/*! Convert the contents of the DataBuffer to a string
- \param nullTerminates Whether the decoder should stop and return the string after encountering a null (\x00) byte.
+ \param nullTerminates Whether the decoder should stop and return the string after encountering a null (\x00)
+ byte.
- @threadunsafe
+ @threadunsafe
*/
- std::string ToEscapedString(bool nullTerminates = false) const;
+ std::string ToEscapedString(bool nullTerminates = false, bool escapePrintable = false) const;
/*! Create a DataBuffer from a given escaped string.
- \param src Input string
- \returns Databuffer created from this string
+ \param src Input string
+ \returns Databuffer created from this string
*/
static DataBuffer FromEscapedString(const std::string& src);
@@ -2397,7 +2398,9 @@ namespace BinaryNinja {
InstructionTextToken(const BNInstructionTextToken& token);
InstructionTextToken WithConfidence(uint8_t conf);
+ static void ConvertInstructionTextToken(const InstructionTextToken& token, BNInstructionTextToken* result);
static BNInstructionTextToken* CreateInstructionTextTokenList(const std::vector<InstructionTextToken>& tokens);
+ static void FreeInstructionTextToken(BNInstructionTextToken* token);
static void FreeInstructionTextTokenList(
BNInstructionTextToken* tokens, size_t count);
static std::vector<InstructionTextToken> ConvertAndFreeInstructionTextTokenList(
@@ -3956,6 +3959,29 @@ namespace BinaryNinja {
static Ref<Symbol> ImportedFunctionFromImportAddressSymbol(Symbol* sym, uint64_t addr);
};
+ struct FunctionViewType
+ {
+ BNFunctionGraphType type;
+ std::string name;
+
+ FunctionViewType() : type(NormalFunctionGraph) {}
+ FunctionViewType(BNFunctionGraphType viewType);
+ FunctionViewType(const std::string& langName) :
+ type(HighLevelLanguageRepresentationFunctionGraph), name(langName)
+ {}
+ FunctionViewType(const BNFunctionViewType& viewType);
+
+ BNFunctionViewType ToAPIObject() const;
+
+ BNFunctionGraphType GetBackingILType() const;
+
+ bool IsValidForView(BinaryView* view) const;
+
+ bool operator==(const FunctionViewType& other) const;
+ bool operator!=(const FunctionViewType& other) const;
+ bool operator<(const FunctionViewType& other) const;
+ };
+
// TODO: This describes how the xref source references the target
enum ReferenceType
{
@@ -6363,29 +6389,29 @@ namespace BinaryNinja {
bool FindNextData(
uint64_t start, const DataBuffer& data, uint64_t& result, BNFindFlag flags = FindCaseSensitive);
bool FindNextText(uint64_t start, const std::string& data, uint64_t& result, Ref<DisassemblySettings> settings,
- BNFindFlag flags = FindCaseSensitive, BNFunctionGraphType graph = NormalFunctionGraph);
+ BNFindFlag flags = FindCaseSensitive, const FunctionViewType& viewType = NormalFunctionGraph);
bool FindNextConstant(uint64_t start, uint64_t constant, uint64_t& result, Ref<DisassemblySettings> settings,
- BNFunctionGraphType graph = NormalFunctionGraph);
+ const FunctionViewType& viewType = NormalFunctionGraph);
bool FindNextData(uint64_t start, uint64_t end, const DataBuffer& data, uint64_t& addr, BNFindFlag flags,
const std::function<bool(size_t current, size_t total)>& progress);
bool FindNextText(uint64_t start, uint64_t end, const std::string& data, uint64_t& addr,
- Ref<DisassemblySettings> settings, BNFindFlag flags, BNFunctionGraphType graph,
+ Ref<DisassemblySettings> settings, BNFindFlag flags, const FunctionViewType& viewType,
const std::function<bool(size_t current, size_t total)>& progress);
bool FindNextConstant(uint64_t start, uint64_t end, uint64_t constant, uint64_t& addr,
- Ref<DisassemblySettings> settings, BNFunctionGraphType graph,
+ Ref<DisassemblySettings> settings, const FunctionViewType& viewType,
const std::function<bool(size_t current, size_t total)>& progress);
bool FindAllData(uint64_t start, uint64_t end, const DataBuffer& data, BNFindFlag flags,
const std::function<bool(size_t current, size_t total)>& progress,
const std::function<bool(uint64_t addr, const DataBuffer& match)>& matchCallback);
bool FindAllText(uint64_t start, uint64_t end, const std::string& data, Ref<DisassemblySettings> settings,
- BNFindFlag flags, BNFunctionGraphType graph,
+ BNFindFlag flags, const FunctionViewType& viewType,
const std::function<bool(size_t current, size_t total)>& progress,
const std::function<bool(uint64_t addr, const std::string& match, const LinearDisassemblyLine& line)>&
matchCallback);
bool FindAllConstant(uint64_t start, uint64_t end, uint64_t constant, Ref<DisassemblySettings> settings,
- BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& progress,
+ const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress,
const std::function<bool(uint64_t addr, const LinearDisassemblyLine& line)>& matchCallback);
bool Search(const std::string& query, const std::function<bool(uint64_t offset, const DataBuffer& buffer)>& otherCallback);
@@ -6819,6 +6845,9 @@ namespace BinaryNinja {
bool UserGlobalPointerValueSet() const;
void ClearUserGlobalPointerValue();
void SetUserGlobalPointerValue(const Confidence<RegisterValue>& value);
+
+ std::optional<std::pair<std::string, BNStringType>> StringifyUnicodeData(
+ Architecture* arch, const DataBuffer& buffer, bool allowShortStrings = false);
};
class MemoryMap
@@ -8801,8 +8830,11 @@ namespace BinaryNinja {
Confidence<int64_t> GetStackAdjustment() const;
QualifiedName GetStructureName() const;
Ref<NamedTypeReference> GetRegisteredName() const;
+ std::string GetAlternateName() const;
uint32_t GetSystemCallNumber() const;
BNIntegerDisplayType GetIntegerTypeDisplayType() const;
+ BNNameType GetNameType() const;
+ bool ShouldDisplayReturnType() const;
uint64_t GetElementCount() const;
uint64_t GetOffset() const;
@@ -9500,6 +9532,11 @@ namespace BinaryNinja {
Ref<Structure> WithReplacedStructure(Structure* from, Structure* to);
Ref<Structure> WithReplacedEnumeration(Enumeration* from, Enumeration* to);
Ref<Structure> WithReplacedNamedTypeReference(NamedTypeReference* from, NamedTypeReference* to);
+
+ bool ResolveMemberOrBaseMember(BinaryView* data, uint64_t offset, size_t size,
+ const std::function<void(NamedTypeReference* baseName, Structure* s, size_t memberIndex,
+ uint64_t structOffset, uint64_t adjustedOffset, const StructureMember& member)>& resolveFunc,
+ std::optional<size_t> memberIndexHint = std::nullopt);
};
/*! StructureBuilder is a convenience class used for building Structure Types.
@@ -10419,7 +10456,7 @@ namespace BinaryNinja {
ConstantData(BNRegisterValueType state, uint64_t value);
ConstantData(BNRegisterValueType state, uint64_t value, size_t size, Ref<Function> func = nullptr);
- DataBuffer ToDataBuffer() const;
+ std::pair<DataBuffer, BNBuiltinType> ToDataBuffer() const;
RegisterValue ToRegisterValue() const;
};
@@ -10675,7 +10712,8 @@ namespace BinaryNinja {
std::set<size_t> GetLowLevelILInstructionsForAddress(Architecture* arch, uint64_t addr);
std::vector<size_t> GetLowLevelILExitsForInstruction(Architecture* arch, uint64_t addr);
- DataBuffer GetConstantData(BNRegisterValueType state, uint64_t value, size_t size = 0);
+ std::pair<DataBuffer, BNBuiltinType> GetConstantData(
+ BNRegisterValueType state, uint64_t value, size_t size = 0);
RegisterValue GetRegisterValueAtInstruction(Architecture* arch, uint64_t addr, uint32_t reg);
RegisterValue GetRegisterValueAfterInstruction(Architecture* arch, uint64_t addr, uint32_t reg);
@@ -10765,8 +10803,9 @@ namespace BinaryNinja {
\return The HLIL for this Function if it's available.
*/
Ref<HighLevelILFunction> GetHighLevelILIfAvailable() const;
- Ref<LanguageRepresentationFunction> GetLanguageRepresentation() const;
- Ref<LanguageRepresentationFunction> GetLanguageRepresentationIfAvailable() const;
+ Ref<LanguageRepresentationFunction> GetLanguageRepresentation(const std::string& language = "Pseudo C") const;
+ Ref<LanguageRepresentationFunction> GetLanguageRepresentationIfAvailable(
+ const std::string& language = "Pseudo C") const;
Ref<Type> GetType() const;
Confidence<Ref<Type>> GetReturnType() const;
@@ -10807,7 +10846,7 @@ namespace BinaryNinja {
void ApplyImportedTypes(Symbol* sym, Ref<Type> type = nullptr);
void ApplyAutoDiscoveredType(Type* type);
- Ref<FlowGraph> CreateFunctionGraph(BNFunctionGraphType type, DisassemblySettings* settings = nullptr);
+ Ref<FlowGraph> CreateFunctionGraph(const FunctionViewType& type, DisassemblySettings* settings = nullptr);
std::map<int64_t, std::vector<VariableNameAndType>> GetStackLayout();
void CreateAutoStackVariable(int64_t offset, const Confidence<Ref<Type>>& type, const std::string& name);
@@ -13280,6 +13319,7 @@ namespace BinaryNinja {
};
struct HighLevelILInstruction;
+ class HighLevelILTokenEmitter;
/*!
\ingroup highlevelil
@@ -13512,6 +13552,8 @@ namespace BinaryNinja {
bool IsSSAVarLive(const SSAVariable& var) const;
bool IsSSAVarLiveAt(const SSAVariable& var, const size_t instr) const;
bool IsVarLiveAt(const Variable& var, const size_t instr) const;
+ static bool HasSideEffects(const HighLevelILInstruction& instr);
+ static BNScopeType GetExprScopeType(const HighLevelILInstruction& instr);
std::set<size_t> GetVariableSSAVersions(const Variable& var) const;
std::set<size_t> GetVariableDefinitions(const Variable& var) const;
@@ -13559,15 +13601,254 @@ namespace BinaryNinja {
size_t GetExprIndexForLabel(uint64_t label);
std::set<size_t> GetUsesForLabel(uint64_t label);
+
+ std::set<Variable> GetVariables();
+ std::set<Variable> GetAliasedVariables();
+ std::set<SSAVariable> GetSSAVariables();
};
+ /*! LanguageRepresentationFunction represents a single function in a registered high level language.
+
+ \ingroup highlevelil
+ */
class LanguageRepresentationFunction :
public CoreRefCountObject<BNLanguageRepresentationFunction, BNNewLanguageRepresentationFunctionReference,
BNFreeLanguageRepresentationFunction>
{
- public:
- LanguageRepresentationFunction(Architecture* arch, Function* func = nullptr);
+ public:
+ LanguageRepresentationFunction(Architecture* arch, Function* func, HighLevelILFunction* highLevelIL);
LanguageRepresentationFunction(BNLanguageRepresentationFunction* func);
+
+ /*! Gets the lines of tokens for a given High Level IL instruction.
+
+ \param instr The instruction to emit lines for.
+ \param settings The settings for disassembly (optional).
+ \param asFullAst Whether to emit full AST or single expressions.
+ \param precedence The current operator precedence level.
+ \param statement Whether the instruction is a statement or an expression.
+ \return A list of lines of tokens for the instruction.
+ */
+ std::vector<DisassemblyTextLine> GetExprText(const HighLevelILInstruction& instr, DisassemblySettings* settings,
+ bool asFullAst = true, BNOperatorPrecedence precedence = TopLevelOperatorPrecedence,
+ bool statement = false);
+
+ /*! Generates lines for the given High Level IL instruction in the style of the linear view. To get the lines
+ for the entire function, pass the root instruction of a HighLevelILFunction.
+
+ \param instr The instruction to emit lines for.
+ \param settings The settings for disassembly (optional).
+ \param asFullAst Whether to emit full AST or single expressions.
+ \return A list of lines of tokens for the instruction.
+ */
+ std::vector<DisassemblyTextLine> GetLinearLines(
+ const HighLevelILInstruction& instr, DisassemblySettings* settings, bool asFullAst = true);
+
+ /*! Generates lines for a single High Level IL basic block.
+
+ \param block The basic block to emit lines for.
+ \param settings The settings for disassembly (optional).
+ \return A list of lines of tokens for the basic block.
+ */
+ std::vector<DisassemblyTextLine> GetBlockLines(BasicBlock* block, DisassemblySettings* settings);
+
+ /*! Gets the highlight color for a given basic block.
+
+ \param block The basic block to get the highlight color for.
+ \return The highlight color for the basic block.
+ */
+ BNHighlightColor GetHighlight(BasicBlock* block);
+
+ Ref<Architecture> GetArchitecture() const;
+ Ref<Function> GetFunction() const;
+ Ref<HighLevelILFunction> GetHighLevelILFunction() const;
+
+ /*! Gets the string representing the start of a comment.
+
+ \return The string representing the start of a comment.
+ */
+ virtual std::string GetCommentStartString() const { return "// "; }
+
+ /*! Gets the string representing the end of a comment.
+
+ \return The string representing the end of a comment.
+ */
+ virtual std::string GetCommentEndString() const { return ""; }
+
+ /*! Gets the string representing the start of an annotation.
+
+ \return The string representing the start of an annotation.
+ */
+ virtual std::string GetAnnotationStartString() const { return "{"; }
+
+ /*! Gets the string representing the end of an annotation.
+
+ \return The string representing the end of an annotation.
+ */
+ virtual std::string GetAnnotationEndString() const { return "}"; }
+
+ protected:
+ /*! Override this method to initialize the options for the token emitter before it is used.
+
+ \param tokens The token emitter to initialize.
+ */
+ virtual void InitTokenEmitter(HighLevelILTokenEmitter& tokens);
+
+ /*! This method must be overridden by all language representation plugins.
+
+ This method is called to emit the tokens for a given High Level IL instruction.
+
+ \param instr The instruction to emit tokens for.
+ \param tokens The token emitter to use.
+ \param settings The disassembly settings to use (may be NULL).
+ \param asFullAst Whether to emit full AST or single expressions.
+ \param precedence The current operator precedence level.
+ \param statement Whether the instruction is a statement or an expression.
+ */
+ virtual void GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, bool asFullAst = true,
+ BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false) = 0;
+
+ /*! This method can be overridden to emit tokens at the start of a function.
+
+ \param instr The root instruction of the function.
+ \param tokens The token emitter to use.
+ */
+ virtual void BeginLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens);
+
+ /*! This method can be overridden to emit tokens at the end of a function.
+
+ \param instr The root instruction of the function.
+ \param tokens The token emitter to use.
+ */
+ virtual void EndLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens);
+
+ private:
+ static void FreeCallback(void* ctxt);
+ static void InitTokenEmitterCallback(void* ctxt, BNHighLevelILTokenEmitter* tokens);
+ static void GetExprTextCallback(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex,
+ BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, bool statement);
+ static void BeginLinesCallback(
+ void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens);
+ static void EndLinesCallback(
+ void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens);
+ static char* GetCommentStartStringCallback(void* ctxt);
+ static char* GetCommentEndStringCallback(void* ctxt);
+ static char* GetAnnotationStartStringCallback(void* ctxt);
+ static char* GetAnnotationEndStringCallback(void* ctxt);
+ };
+
+ /*!
+ \ingroup highlevelil
+ */
+ class CoreLanguageRepresentationFunction : public LanguageRepresentationFunction
+ {
+ public:
+ CoreLanguageRepresentationFunction(BNLanguageRepresentationFunction* func);
+ std::string GetCommentStartString() const override;
+ std::string GetCommentEndString() const override;
+ std::string GetAnnotationStartString() const override;
+ std::string GetAnnotationEndString() const override;
+
+ protected:
+ void GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, bool asFullAst = true,
+ BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false) override;
+ };
+
+ class TypePrinter;
+ class TypeParser;
+
+ /*! LanguageRepresentationFunctionType represents a custom language representation function type.
+ This class provides methods to create LanguageRepresentationFunction instances for functions, as well
+ as manage the printing and parsing of types.
+
+ \ingroup highlevelil
+ */
+ class LanguageRepresentationFunctionType : public StaticCoreRefCountObject<BNLanguageRepresentationFunctionType>
+ {
+ std::string m_nameForRegister;
+
+ public:
+ LanguageRepresentationFunctionType(const std::string& name);
+ LanguageRepresentationFunctionType(BNLanguageRepresentationFunctionType* type);
+
+ std::string GetName() const;
+
+ /*! This method must be overridden. This creates the LanguageRepresentationFunction object for the
+ given architecture, owner function, and High Level IL function.
+
+ \param arch The architecture of the function.
+ \param owner The associated function.
+ \param highLevelIL The High Level IL for the function.
+ \return A LanguageRepresentationFunction instance for the given function.
+ */
+ virtual Ref<LanguageRepresentationFunction> Create(
+ Architecture* arch, Function* owner, HighLevelILFunction* highLevelIL) = 0;
+
+ /*! Returns whether the language is valid for the given binary view.
+
+ \param view The binary view to check the validity for.
+ \return True if the language is valid for the given binary view, false otherwise.
+ */
+ virtual bool IsValid(BinaryView* view);
+
+ /*! Returns the type printer for displaying types in this language. If NULL is returned, the default type
+ printer will be used.
+
+ \return The optional type printer for displaying types in this language.
+ */
+ virtual Ref<TypePrinter> GetTypePrinter() { return nullptr; }
+
+ /*! Returns the type parser for parsing types in this language. If NULL is returned, the default type
+ parser will be used.
+
+ \return The optional type parser for parsing types in this language.
+ */
+ virtual Ref<TypeParser> GetTypeParser() { return nullptr; }
+
+ /*! Returns a list of lines representing a function prototype in this language. If no lines are returned, the
+ default C-style prototype will be used.
+
+ \param func The function to get the prototype lines for.
+ \param settings The disassembly settings to use (may be NULL).
+ \return An optional vector of lines representing the function prototype.
+ */
+ virtual std::vector<DisassemblyTextLine> GetFunctionTypeTokens(
+ Function* func, DisassemblySettings* settings = nullptr);
+
+ /*! Registers the language representation function type.
+
+ \param type The language representation function type to register.
+ */
+ static void Register(LanguageRepresentationFunctionType* type);
+
+ static Ref<LanguageRepresentationFunctionType> GetByName(const std::string& name);
+ static bool IsValidByName(const std::string& name, BinaryView* view);
+ static std::vector<Ref<LanguageRepresentationFunctionType>> GetTypes();
+
+ private:
+ static BNLanguageRepresentationFunction* CreateCallback(
+ void* ctxt, BNArchitecture* arch, BNFunction* owner, BNHighLevelILFunction* highLevelIL);
+ static bool IsValidCallback(void* ctxt, BNBinaryView* view);
+ static BNTypePrinter* GetTypePrinterCallback(void* ctxt);
+ static BNTypeParser* GetTypeParserCallback(void* ctxt);
+ static BNDisassemblyTextLine* GetFunctionTypeTokensCallback(
+ void* ctxt, BNFunction* func, BNDisassemblySettings* settings, size_t* count);
+ static void FreeLinesCallback(void* ctxt, BNDisassemblyTextLine* lines, size_t count);
+ };
+
+ class CoreLanguageRepresentationFunctionType : public LanguageRepresentationFunctionType
+ {
+ public:
+ CoreLanguageRepresentationFunctionType(BNLanguageRepresentationFunctionType* type);
+ Ref<LanguageRepresentationFunction> Create(
+ Architecture* arch, Function* owner, HighLevelILFunction* highLevelIL) override;
+ bool IsValid(BinaryView* view) override;
+ Ref<TypePrinter> GetTypePrinter() override;
+ Ref<TypeParser> GetTypeParser() override;
+ std::vector<DisassemblyTextLine> GetFunctionTypeTokens(
+ Function* func, DisassemblySettings* settings = nullptr) override;
};
/*!
@@ -16431,7 +16712,7 @@ namespace BinaryNinja {
void* ctxt, BNBinaryView* data, uint64_t addr, BNType* type, BNTypeContext* typeCtx, size_t ctxCount);
static BNDisassemblyTextLine* GetLinesForDataCallback(void* ctxt, BNBinaryView* data, uint64_t addr,
BNType* type, const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count,
- BNTypeContext* typeCxt, size_t ctxCount);
+ BNTypeContext* typeCxt, size_t ctxCount, const char* language);
static void FreeCallback(void* ctxt);
static void FreeLinesCallback(void* ctxt, BNDisassemblyTextLine* lines, size_t count);
@@ -16442,10 +16723,10 @@ namespace BinaryNinja {
BinaryView* data, uint64_t addr, Type* type, std::vector<std::pair<Type*, size_t>>& context);
virtual std::vector<DisassemblyTextLine> GetLinesForData(BinaryView* data, uint64_t addr, Type* type,
const std::vector<InstructionTextToken>& prefix, size_t width,
- std::vector<std::pair<Type*, size_t>>& context);
+ std::vector<std::pair<Type*, size_t>>& context, const std::string& language = std::string());
std::vector<DisassemblyTextLine> RenderLinesForData(BinaryView* data, uint64_t addr, Type* type,
const std::vector<InstructionTextToken>& prefix, size_t width,
- std::vector<std::pair<Type*, size_t>>& context);
+ std::vector<std::pair<Type*, size_t>>& context, const std::string& language = std::string());
static bool IsStructOfTypeName(
Type* type, const QualifiedName& name, std::vector<std::pair<Type*, size_t>>& context);
@@ -16505,6 +16786,12 @@ namespace BinaryNinja {
void ResetDeduplicatedComments();
bool AddSymbolToken(std::vector<InstructionTextToken>& tokens, uint64_t addr, size_t size, size_t operand);
+ static BNSymbolDisplayResult AddSymbolTokenStatic(
+ std::vector<InstructionTextToken>& tokens, uint64_t addr, size_t size, size_t operand,
+ BinaryView* data, size_t maxSymbolWidth, Function* func, uint8_t confidence = BN_FULL_CONFIDENCE,
+ BNSymbolDisplayType symbolDisplay = DisplaySymbolOnly,
+ BNOperatorPrecedence precedence = TopLevelOperatorPrecedence,
+ uint64_t instrAddr = -1, uint64_t exprIndex = -1);
void AddStackVariableReferenceTokens(
std::vector<InstructionTextToken>& tokens, const StackVariableReference& ref);
@@ -16516,6 +16803,7 @@ namespace BinaryNinja {
bool hasAutoAnnotations, const std::string& leadingSpaces = " ", const std::string& indentSpaces = "");
static std::string GetDisplayStringForInteger(Ref<BinaryView> binaryView, BNIntegerDisplayType type,
uint64_t value, size_t inputWidth, bool isSigned = true);
+ static std::string GetStringLiteralPrefix(BNStringType type);
};
/*!
@@ -16573,7 +16861,8 @@ namespace BinaryNinja {
static Ref<LinearViewObject> CreateMappedMediumLevelILSSAForm(BinaryView* view, DisassemblySettings* settings);
static Ref<LinearViewObject> CreateHighLevelIL(BinaryView* view, DisassemblySettings* settings);
static Ref<LinearViewObject> CreateHighLevelILSSAForm(BinaryView* view, DisassemblySettings* settings);
- static Ref<LinearViewObject> CreateLanguageRepresentation(BinaryView* view, DisassemblySettings* settings);
+ static Ref<LinearViewObject> CreateLanguageRepresentation(BinaryView* view, DisassemblySettings* settings,
+ const std::string& language = "Pseudo C");
static Ref<LinearViewObject> CreateDataOnly(BinaryView* view, DisassemblySettings* settings);
static Ref<LinearViewObject> CreateSingleFunctionDisassembly(Function* func, DisassemblySettings* settings);
@@ -16592,7 +16881,7 @@ namespace BinaryNinja {
static Ref<LinearViewObject> CreateSingleFunctionHighLevelILSSAForm(
Function* func, DisassemblySettings* settings);
static Ref<LinearViewObject> CreateSingleFunctionLanguageRepresentation(
- Function* func, DisassemblySettings* settings);
+ Function* func, DisassemblySettings* settings, const std::string& language = "Pseudo C");
};
/*!
@@ -16669,7 +16958,7 @@ namespace BinaryNinja {
{
BNFindType type;
BNFindRangeType rangeType;
- BNFunctionGraphType ilType;
+ FunctionViewType ilType;
std::string string;
BNFindFlag flags;
bool findAll;
@@ -18106,6 +18395,210 @@ namespace BinaryNinja {
const size_t dataLen
);
} // namespace Unicode
+
+ /*! HighLevelILTokenEmitter contains methods for emitting text tokens for High Level IL instructions.
+ Methods are provided for typical patterns found in various high level languages.
+
+ This class cannot be instantiated directly. An instance of the class will be provided when the methods
+ in LanguageRepresentationFunction are called.
+
+ \ingroup highlevelil
+ */
+ class HighLevelILTokenEmitter:
+ public CoreRefCountObject<BNHighLevelILTokenEmitter, BNNewHighLevelILTokenEmitterReference, BNFreeHighLevelILTokenEmitter>
+ {
+ public:
+ HighLevelILTokenEmitter(BNHighLevelILTokenEmitter* emitter);
+
+ class CurrentExprGuard
+ {
+ HighLevelILTokenEmitter* m_parent;
+ BNTokenEmitterExpr m_expr;
+
+ CurrentExprGuard(const CurrentExprGuard&) = delete;
+ CurrentExprGuard& operator=(const CurrentExprGuard&) = delete;
+
+ public:
+ CurrentExprGuard(HighLevelILTokenEmitter& parent, const BNTokenEmitterExpr& expr);
+ ~CurrentExprGuard();
+ };
+
+ /*! Appends a token to the output. */
+ template <typename... Args>
+ void Append(Args&&... args)
+ {
+ InstructionTextToken token(std::forward<Args>(args)...);
+ BNInstructionTextToken converted;
+ InstructionTextToken::ConvertInstructionTextToken(token, &converted);
+ BNHighLevelILTokenEmitterAppend(m_object, &converted);
+ InstructionTextToken::FreeInstructionTextToken(&converted);
+ }
+
+ /*! Starts a new line in the output. */
+ void NewLine();
+
+ /*! Increases the indentation level by one. */
+ void IncreaseIndent();
+
+ /*! Decreases the indentation level by one. */
+ void DecreaseIndent();
+
+ /*! Indicates that visual separation of scopes is desirable at the current position. By default,
+ this will insert a blank line, but this can be configured by the user.
+ */
+ void ScopeSeparator();
+
+ /*! Begins a new scope. Insertion of newlines and braces will be handled using the current settings.
+
+ \param scopeType Type of scope to be started.
+ */
+ void BeginScope(BNScopeType scopeType);
+
+ /*! Ends the current scope.
+
+ \param scopeType Type of scope passed to BeginScope.
+ */
+ void EndScope(BNScopeType scopeType);
+
+ /*! Continues the previous scope with a new associated scope. This is most commonly used for else statements.
+
+ \param forceSameLine If true, the continuation will always be placed on the same line as the previous scope.
+ */
+ void ScopeContinuation(bool forceSameLine);
+
+ /*! Finalizes the previous scope, indicating that there are no more associated scopes. */
+ void FinalizeScope();
+
+ /*! Forces there to be no indentation for the next line. */
+ void NoIndentForThisLine();
+
+ /*! Begins a region of tokens that always have zero confidence. */
+ void BeginForceZeroConfidence();
+
+ /*! Ends a region of tokens that always have zero confidence. */
+ void EndForceZeroConfidence();
+
+ /*! Sets the current expression. When the returned guard object goes out of scope, the previously set
+ expression becomes active again.
+
+ \param expr Expression to set as the current expression.
+ \return Guard object to manage the current expression.
+ */
+ CurrentExprGuard SetCurrentExpr(const HighLevelILInstruction& expr);
+
+ /*! Finalizes the outputted lines. */
+ void Finalize();
+
+ void AppendOpenParen(); // (
+ void AppendCloseParen(); // )
+ void AppendOpenBracket(); // [
+ void AppendCloseBracket(); // ]
+ void AppendOpenBrace(); // {
+ void AppendCloseBrace(); // }
+ void AppendSemicolon();
+
+ /*! Returns the list of tokens on the current line */
+ std::vector<InstructionTextToken> GetCurrentTokens() const;
+
+ /*! Sets the requirement for insertion of braces around scopes in the output. */
+ void SetBraceRequirement(BNBraceRequirement required);
+
+ /*! Sets whether cases within switch statements should always have braces around them. */
+ void SetBracesAroundSwitchCases(bool braces);
+
+ /*! Sets whether braces should default to being on the same line as the statement that begins the scope.
+ If the user has explicitly set a preference, this setting will be ignored and the user's preference
+ will be used instead.
+ */
+ void SetDefaultBracesOnSameLine(bool sameLine);
+
+ /*! Sets whether omitting braces around single-line scopes is allowed. */
+ void SetSimpleScopeAllowed(bool allowed);
+
+ BNBraceRequirement GetBraceRequirement() const;
+ bool HasBracesAroundSwitchCases() const;
+ bool GetDefaultBracesOnSameLine() const;
+ bool IsSimpleScopeAllowed() const;
+
+ /*! Gets the list of lines in the output. */
+ std::vector<DisassemblyTextLine> GetLines() const;
+
+ /*! Appends a size token for the given size in the High Level IL syntax.
+
+ \param size Size in bytes.
+ \param type Token type to append.
+ */
+ void AppendSizeToken(size_t size, BNInstructionTextTokenType type);
+
+ /*! Appends a floating point size token for the given size in the High Level IL syntax.
+
+ \param size Size in bytes.
+ \param type Token type to append.
+ */
+ void AppendFloatSizeToken(size_t size, BNInstructionTextTokenType type);
+
+ /*! Appends tokens for access to a variable.
+
+ \param var Variable to access.
+ \param instr Instruction that accesses the variable.
+ \param size Size in bytes.
+ */
+ void AppendVarTextToken(const Variable& var, const HighLevelILInstruction& instr, size_t size);
+
+ /*! Appends tokens for a constant intenger value.
+
+ \param instr Instruction that references the value.
+ \param val Integer value.
+ \param size Size in bytes.
+ */
+ void AppendIntegerTextToken(const HighLevelILInstruction& instr, int64_t val, size_t size);
+
+ /*! Appends tokens for accessing an array by constant index.
+
+ \param instr Instruction that accesses the array.
+ \param val Index value.
+ \param size Size in bytes.
+ \param address Optional address override.
+ */
+ void AppendArrayIndexToken(const HighLevelILInstruction& instr, int64_t val, size_t size, uint64_t address = 0);
+
+ /*! Appends tokens for displaying a constant pointer value.
+
+ \param instr Instruction that references the pointer.
+ \param val Pointer value.
+ \param settings Settings for disassembly (may be NULL).
+ \param symbolDisplay Symbol display type.
+ \param precedence Current operator precedence level.
+ \param allowShortString If true, show as a string even if it is short.
+ \return Type of symbol resolved if any.
+ */
+ BNSymbolDisplayResult AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val,
+ DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence,
+ bool allowShortString = false);
+
+ /*! Appends tokens for a constant value.
+
+ \param instr Instruction that references the value.
+ \param val Constant value.
+ \param size Size in bytes.
+ \param settings Settings for disassembly (may be NULL).
+ \param precedence Current operator precedence level.
+ */
+ void AppendConstantTextToken(const HighLevelILInstruction& instr, int64_t val, size_t size,
+ DisassemblySettings* settings, BNOperatorPrecedence precedence);
+
+ /*! Prepends the list of names for the outer structure members when accessing a structure member. This list
+ can be passed as the list of type names in tokens.
+
+ \param data Binary view associated with the type.
+ \param type Structure Type being accessed.
+ \param var Structure variable.
+ \param nameList Existing list of member names. This list will be updated with the outer structure member
+ names.
+ */
+ static void AddNamesForOuterStructureMembers(
+ BinaryView* data, Type* type, const HighLevelILInstruction& var, std::vector<std::string>& nameList);
+ };
} // namespace BinaryNinja
diff --git a/binaryninjacore.h b/binaryninjacore.h
index aaf61c6d..93f97dd1 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -37,14 +37,14 @@
// Current ABI version for linking to the core. This is incremented any time
// there are changes to the API that affect linking, including new functions,
// new types, or modifications to existing functions or types.
-#define BN_CURRENT_CORE_ABI_VERSION 78
+#define BN_CURRENT_CORE_ABI_VERSION 79
// Minimum ABI version that is supported for loading of plugins. Plugins that
// are linked to an ABI version less than this will not be able to load and
// will require rebuilding. The minimum version is increased when there are
// incompatible changes that break binary compatibility, such as changes to
// existing types or functions.
-#define BN_MINIMUM_CORE_ABI_VERSION 75
+#define BN_MINIMUM_CORE_ABI_VERSION 79
#ifdef __GNUC__
#ifdef BINARYNINJACORE_LIBRARY
@@ -224,6 +224,8 @@ extern "C"
typedef struct BNMediumLevelILFunction BNMediumLevelILFunction;
typedef struct BNHighLevelILFunction BNHighLevelILFunction;
typedef struct BNLanguageRepresentationFunction BNLanguageRepresentationFunction;
+ typedef struct BNLanguageRepresentationFunctionType BNLanguageRepresentationFunctionType;
+ typedef struct BNHighLevelILTokenEmitter BNHighLevelILTokenEmitter;
typedef struct BNType BNType;
typedef struct BNTypeBuilder BNTypeBuilder;
typedef struct BNTypeLibrary BNTypeLibrary;
@@ -694,6 +696,12 @@ extern "C"
HighLevelLanguageRepresentationFunctionGraph = 10,
} BNFunctionGraphType;
+ typedef struct BNFunctionViewType
+ {
+ BNFunctionGraphType type;
+ const char* name; // Only used for HighLevelLanguageRepresentationFunctionGraph
+ } BNFunctionViewType;
+
typedef enum BNDisassemblyOption
{
ShowAddress = 0,
@@ -2992,7 +3000,7 @@ extern "C"
void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, BNTypeContext* typeCtx, size_t ctxCount);
BNDisassemblyTextLine* (*getLinesForData)(void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type,
const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count,
- BNTypeContext* typeCtx, size_t ctxCount);
+ BNTypeContext* typeCtx, size_t ctxCount, const char* language);
void (*freeLines)(void* ctx, BNDisassemblyTextLine* lines, size_t count);
} BNCustomDataRenderer;
@@ -3335,6 +3343,108 @@ extern "C"
void (*freeVarName)(void* ctxt, BNQualifiedName* name);
} BNDemanglerCallbacks;
+ typedef enum BNScopeType
+ {
+ OneLineScopeType,
+ HasSubScopeScopeType,
+ BlockScopeType,
+ SwitchScopeType,
+ CaseScopeType
+ } BNScopeType;
+
+ typedef enum BNBraceRequirement
+ {
+ OptionalBraces,
+ BracesNotAllowed,
+ BracesAlwaysRequired
+ } BNBraceRequirement;
+
+ typedef struct BNTokenEmitterExpr
+ {
+ uint64_t address;
+ uint32_t sourceOperand;
+ size_t exprIndex;
+ size_t instrIndex;
+ } BNTokenEmitterExpr;
+
+ typedef enum BNOperatorPrecedence
+ {
+ TopLevelOperatorPrecedence = 0,
+ AssignmentOperatorPrecedence,
+ TernaryOperatorPrecedence,
+ LogicalOrOperatorPrecedence,
+ LogicalAndOperatorPrecedence,
+ BitwiseOrOperatorPrecedence,
+ BitwiseXorOperatorPrecedence,
+ BitwiseAndOperatorPrecedence,
+ EqualityOperatorPrecedence,
+ CompareOperatorPrecedence,
+ ShiftOperatorPrecedence,
+ AddOperatorPrecedence,
+ SubOperatorPrecedence,
+ MultiplyOperatorPrecedence,
+ DivideOperatorPrecedence,
+ LowUnaryOperatorPrecedence,
+ UnaryOperatorPrecedence,
+ HighUnaryOperatorPrecedence,
+ MemberAndFunctionOperatorPrecedence,
+ ScopeOperatorPrecedence
+ } BNOperatorPrecedence;
+
+ typedef enum BNSymbolDisplayType
+ {
+ DisplaySymbolOnly,
+ AddressOfDataSymbols,
+ DereferenceNonDataSymbols
+ } BNSymbolDisplayType;
+
+ typedef enum BNSymbolDisplayResult
+ {
+ NoSymbolAvailable,
+ DataSymbolResult,
+ OtherSymbolResult
+ } BNSymbolDisplayResult;
+
+ typedef struct BNCustomLanguageRepresentationFunction
+ {
+ void* context;
+ void (*freeObject)(void* ctxt);
+ void (*externalRefTaken)(void* ctxt);
+ void (*externalRefReleased)(void* ctxt);
+ void (*initTokenEmitter)(void* ctxt, BNHighLevelILTokenEmitter* tokens);
+ void (*getExprText)(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens,
+ BNDisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement);
+ void (*beginLines)(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens);
+ void (*endLines)(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens);
+ char* (*getCommentStartString)(void* ctxt);
+ char* (*getCommentEndString)(void* ctxt);
+ char* (*getAnnotationStartString)(void* ctxt);
+ char* (*getAnnotationEndString)(void* ctxt);
+ } BNCustomLanguageRepresentationFunction;
+
+ typedef struct BNCustomLanguageRepresentationFunctionType
+ {
+ void* context;
+ BNLanguageRepresentationFunction* (*create)(
+ void* ctxt, BNArchitecture* arch, BNFunction* owner, BNHighLevelILFunction* highLevelIL);
+ bool (*isValid)(void* ctxt, BNBinaryView* view);
+ BNTypePrinter* (*getTypePrinter)(void* ctxt);
+ BNTypeParser* (*getTypeParser)(void* ctxt);
+ BNDisassemblyTextLine* (*getFunctionTypeTokens)(
+ void* ctxt, BNFunction* func, BNDisassemblySettings* settings, size_t* count);
+ void (*freeLines)(void* ctxt, BNDisassemblyTextLine* lines, size_t count);
+ } BNCustomLanguageRepresentationFunctionType;
+
+ typedef enum BNBuiltinType
+ {
+ BuiltinNone,
+ BuiltinMemcpy,
+ BuiltinMemset,
+ BuiltinStrncpy,
+ BuiltinStrcpy,
+ BuiltinWcscpy
+ } BNBuiltinType;
+
typedef bool(*BNProgressFunction)(void*, size_t, size_t);
typedef bool(*BNCollaborationAnalysisConflictHandler)(void*, const char** keys, BNAnalysisMergeConflict** conflicts, size_t conflictCount);
typedef bool(*BNCollaborationNameChangesetFunction)(void*, BNCollaborationChangeset*);
@@ -3504,7 +3614,7 @@ extern "C"
BINARYNINJACOREAPI uint8_t BNGetDataBufferByte(BNDataBuffer* buf, size_t offset);
BINARYNINJACOREAPI void BNSetDataBufferByte(BNDataBuffer* buf, size_t offset, uint8_t val);
- BINARYNINJACOREAPI char* BNDataBufferToEscapedString(BNDataBuffer* buf, bool nullTerminates);
+ BINARYNINJACOREAPI char* BNDataBufferToEscapedString(BNDataBuffer* buf, bool nullTerminates, bool escapePrintable);
BINARYNINJACOREAPI BNDataBuffer* BNDecodeEscapedString(const char* str);
BINARYNINJACOREAPI char* BNDataBufferToBase64(BNDataBuffer* buf);
BINARYNINJACOREAPI BNDataBuffer* BNDecodeBase64(const char* str);
@@ -3918,29 +4028,29 @@ extern "C"
BINARYNINJACOREAPI bool BNFindNextData(
BNBinaryView* view, uint64_t start, BNDataBuffer* data, uint64_t* result, BNFindFlag flags);
BINARYNINJACOREAPI bool BNFindNextText(BNBinaryView* view, uint64_t start, const char* data, uint64_t* result,
- BNDisassemblySettings* settings, BNFindFlag flags, BNFunctionGraphType graph);
+ BNDisassemblySettings* settings, BNFindFlag flags, BNFunctionViewType viewType);
BINARYNINJACOREAPI bool BNFindNextConstant(BNBinaryView* view, uint64_t start, uint64_t constant, uint64_t* result,
- BNDisassemblySettings* settings, BNFunctionGraphType graph);
+ BNDisassemblySettings* settings, BNFunctionViewType viewType);
BINARYNINJACOREAPI bool BNFindNextDataWithProgress(BNBinaryView* view, uint64_t start, uint64_t end,
BNDataBuffer* data, uint64_t* result, BNFindFlag flags, void* ctxt,
BNProgressFunction progress);
BINARYNINJACOREAPI bool BNFindNextTextWithProgress(BNBinaryView* view, uint64_t start, uint64_t end,
const char* data, uint64_t* result, BNDisassemblySettings* settings, BNFindFlag flags,
- BNFunctionGraphType graph, void* ctxt, BNProgressFunction progress);
+ BNFunctionViewType viewType, void* ctxt, BNProgressFunction progress);
BINARYNINJACOREAPI bool BNFindNextConstantWithProgress(BNBinaryView* view, uint64_t start, uint64_t end,
- uint64_t constant, uint64_t* result, BNDisassemblySettings* settings, BNFunctionGraphType graph, void* ctxt,
+ uint64_t constant, uint64_t* result, BNDisassemblySettings* settings, BNFunctionViewType viewType, void* ctxt,
BNProgressFunction progress);
BINARYNINJACOREAPI bool BNFindAllDataWithProgress(BNBinaryView* view, uint64_t start, uint64_t end,
BNDataBuffer* data, BNFindFlag flags, void* ctxt, BNProgressFunction progress,
void* matchCtxt, bool (*matchCallback)(void* matchCtxt, uint64_t addr, BNDataBuffer* match));
BINARYNINJACOREAPI bool BNFindAllTextWithProgress(BNBinaryView* view, uint64_t start, uint64_t end,
- const char* data, BNDisassemblySettings* settings, BNFindFlag flags, BNFunctionGraphType graph, void* ctxt,
+ const char* data, BNDisassemblySettings* settings, BNFindFlag flags, BNFunctionViewType viewType, void* ctxt,
BNProgressFunction progress, void* matchCtxt,
bool (*matchCallback)(void* matchCtxt, uint64_t addr, const char* match, BNLinearDisassemblyLine* line));
BINARYNINJACOREAPI bool BNFindAllConstantWithProgress(BNBinaryView* view, uint64_t start, uint64_t end,
- uint64_t constant, BNDisassemblySettings* settings, BNFunctionGraphType graph, void* ctxt,
+ uint64_t constant, BNDisassemblySettings* settings, BNFunctionViewType viewType, void* ctxt,
BNProgressFunction progress, void* matchCtxt,
bool (*matchCallback)(void* matchCtxt, uint64_t addr, BNLinearDisassemblyLine* line));
@@ -3996,6 +4106,9 @@ extern "C"
BINARYNINJACOREAPI void BNClearUserGlobalPointerValue(BNBinaryView* view);
BINARYNINJACOREAPI void BNSetUserGlobalPointerValue(BNBinaryView* view, BNRegisterValueWithConfidence value);
+ BINARYNINJACOREAPI bool BNStringifyUnicodeData(BNBinaryView* data, BNArchitecture* arch, const BNDataBuffer* buffer,
+ bool allowShortStrings, char** string, BNStringType* type);
+
// Raw binary data view
BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataView(BNFileMetadata* file);
BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataViewFromBuffer(BNFileMetadata* file, BNDataBuffer* buf);
@@ -4370,10 +4483,13 @@ extern "C"
BINARYNINJACOREAPI BNMediumLevelILFunction* BNGetFunctionMappedMediumLevelILIfAvailable(BNFunction* func);
BINARYNINJACOREAPI BNHighLevelILFunction* BNGetFunctionHighLevelIL(BNFunction* func);
BINARYNINJACOREAPI BNHighLevelILFunction* BNGetFunctionHighLevelILIfAvailable(BNFunction* func);
- BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNGetFunctionLanguageRepresentation(BNFunction* func);
- BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNGetFunctionLanguageRepresentationIfAvailable(BNFunction* func);
+ BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNGetFunctionLanguageRepresentation(
+ BNFunction* func, const char* language);
+ BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNGetFunctionLanguageRepresentationIfAvailable(
+ BNFunction* func, const char* language);
- BINARYNINJACOREAPI BNDataBuffer* BNGetConstantData(BNFunction* func, BNRegisterValueType state, uint64_t value, size_t size);
+ BINARYNINJACOREAPI BNDataBuffer* BNGetConstantData(BNFunction* func, BNRegisterValueType state, uint64_t value,
+ size_t size, BNBuiltinType* builtin);
BINARYNINJACOREAPI BNRegisterValue BNGetRegisterValueAtInstruction(
BNFunction* func, BNArchitecture* arch, uint64_t addr, uint32_t reg);
@@ -4556,6 +4672,10 @@ extern "C"
BINARYNINJACOREAPI void BNResetDisassemblyTextRendererDeduplicatedComments(BNDisassemblyTextRenderer* renderer);
BINARYNINJACOREAPI bool BNGetDisassemblyTextRendererSymbolTokens(BNDisassemblyTextRenderer* renderer, uint64_t addr,
size_t size, size_t operand, BNInstructionTextToken** result, size_t* count);
+ BINARYNINJACOREAPI BNSymbolDisplayResult BNGetDisassemblyTextRendererSymbolTokensStatic(uint64_t addr, size_t size,
+ size_t operand, BNBinaryView* data, size_t maxSymbolWidth, BNFunction* func, uint8_t confidence,
+ BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence, uint64_t instrAddr, uint64_t exprIndex,
+ BNInstructionTextToken** result, size_t* count);
BINARYNINJACOREAPI BNInstructionTextToken* BNGetDisassemblyTextRendererStackVariableReferenceTokens(
BNDisassemblyTextRenderer* renderer, BNStackVariableReference* ref, size_t* count);
BINARYNINJACOREAPI bool BNIsIntegerToken(BNInstructionTextTokenType type);
@@ -4565,6 +4685,7 @@ extern "C"
BINARYNINJACOREAPI BNDisassemblyTextLine* BNDisassemblyTextRendererWrapComment(BNDisassemblyTextRenderer* renderer,
const BNDisassemblyTextLine* inLine, size_t* outLineCount, const char* comment, bool hasAutoAnnotations,
const char* leadingSpaces, const char* indentSpaces);
+ BINARYNINJACOREAPI char* BNGetStringLiteralPrefix(BNStringType type);
BINARYNINJACOREAPI void BNMarkFunctionAsRecentlyUsed(BNFunction* func);
BINARYNINJACOREAPI void BNMarkBasicBlockAsRecentlyUsed(BNBasicBlock* block);
@@ -4796,7 +4917,7 @@ extern "C"
BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewHighLevelILSSAForm(
BNBinaryView* view, BNDisassemblySettings* settings);
BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewLanguageRepresentation(
- BNBinaryView* view, BNDisassemblySettings* settings);
+ BNBinaryView* view, BNDisassemblySettings* settings, const char* language);
BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewDataOnly(
BNBinaryView* view, BNDisassemblySettings* settings);
BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewSingleFunctionDisassembly(
@@ -4820,7 +4941,7 @@ extern "C"
BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewSingleFunctionHighLevelILSSAForm(
BNFunction* func, BNDisassemblySettings* settings);
BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewSingleFunctionLanguageRepresentation(
- BNFunction* func, BNDisassemblySettings* settings);
+ BNFunction* func, BNDisassemblySettings* settings, const char* language);
BINARYNINJACOREAPI BNLinearViewObject* BNNewLinearViewObjectReference(BNLinearViewObject* obj);
BINARYNINJACOREAPI void BNFreeLinearViewObject(BNLinearViewObject* obj);
BINARYNINJACOREAPI BNLinearViewObject* BNGetFirstLinearViewObjectChild(BNLinearViewObject* obj);
@@ -5245,7 +5366,7 @@ extern "C"
// Flow graphs
BINARYNINJACOREAPI BNFlowGraph* BNCreateFlowGraph(void);
BINARYNINJACOREAPI BNFlowGraph* BNCreateFunctionGraph(
- BNFunction* func, BNFunctionGraphType type, BNDisassemblySettings* settings);
+ BNFunction* func, BNFunctionViewType type, BNDisassemblySettings* settings);
BINARYNINJACOREAPI BNFlowGraph* BNCreateLowLevelILFunctionGraph(
BNLowLevelILFunction* func, BNDisassemblySettings* settings);
BINARYNINJACOREAPI BNFlowGraph* BNCreateMediumLevelILFunctionGraph(
@@ -5790,6 +5911,8 @@ extern "C"
BNHighLevelILFunction* func, const BNVariable* var, const size_t version, const size_t instr);
BINARYNINJACOREAPI bool BNIsHighLevelILVarLiveAt(
BNHighLevelILFunction* func, const BNVariable* var, const size_t instr);
+ BINARYNINJACOREAPI bool BNHighLevelILHasSideEffects(BNHighLevelILFunction* func, size_t exprIndex);
+ BINARYNINJACOREAPI BNScopeType BNGetHighLevelILExprScopeType(BNHighLevelILFunction* func, size_t exprIndex);
BINARYNINJACOREAPI BNVariable* BNGetHighLevelILVariables(BNHighLevelILFunction* func, size_t* count);
BINARYNINJACOREAPI BNVariable* BNGetHighLevelILAliasedVariables(BNHighLevelILFunction* func, size_t* count);
@@ -5895,12 +6018,49 @@ extern "C"
BNBinaryView* view, const BNQualifiedName* typeName, BNTypeLibrary** lib, BNQualifiedName* resultName);
// Language Representation
+ BINARYNINJACOREAPI BNLanguageRepresentationFunctionType* BNRegisterLanguageRepresentationFunctionType(
+ const char* name, BNCustomLanguageRepresentationFunctionType* type);
+ BINARYNINJACOREAPI BNLanguageRepresentationFunctionType* BNGetLanguageRepresentationFunctionTypeByName(const char* name);
+ BINARYNINJACOREAPI BNLanguageRepresentationFunctionType** BNGetLanguageRepresentationFunctionTypeList(size_t* count);
+ BINARYNINJACOREAPI void BNFreeLanguageRepresentationFunctionTypeList(BNLanguageRepresentationFunctionType** types);
+ BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionTypeName(BNLanguageRepresentationFunctionType* type);
BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNCreateLanguageRepresentationFunction(
- BNArchitecture* arch, BNFunction* func);
+ BNLanguageRepresentationFunctionType* type, BNArchitecture* arch, BNFunction* func,
+ BNHighLevelILFunction* highLevelIL);
+ BINARYNINJACOREAPI bool BNIsLanguageRepresentationFunctionTypeValid(
+ BNLanguageRepresentationFunctionType* type, BNBinaryView* view);
+ BINARYNINJACOREAPI BNTypePrinter* BNGetLanguageRepresentationFunctionTypePrinter(BNLanguageRepresentationFunctionType* type);
+ BINARYNINJACOREAPI BNTypeParser* BNGetLanguageRepresentationFunctionTypeParser(BNLanguageRepresentationFunctionType* type);
+ BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens(
+ BNLanguageRepresentationFunctionType* type, BNFunction* func, BNDisassemblySettings* settings, size_t* count);
+
+ BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNCreateCustomLanguageRepresentationFunction(
+ BNArchitecture* arch, BNFunction* func, BNHighLevelILFunction* highLevelIL,
+ BNCustomLanguageRepresentationFunction* callbacks);
BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNNewLanguageRepresentationFunctionReference(
- BNLanguageRepresentationFunction* func);
+ BNLanguageRepresentationFunction* func);
BINARYNINJACOREAPI void BNFreeLanguageRepresentationFunction(BNLanguageRepresentationFunction* func);
+ BINARYNINJACOREAPI BNArchitecture* BNGetLanguageRepresentationArchitecture(BNLanguageRepresentationFunction* func);
BINARYNINJACOREAPI BNFunction* BNGetLanguageRepresentationOwnerFunction(BNLanguageRepresentationFunction* func);
+ BINARYNINJACOREAPI BNHighLevelILFunction* BNGetLanguageRepresentationILFunction(BNLanguageRepresentationFunction* func);
+ BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLanguageRepresentationFunctionExprText(
+ BNLanguageRepresentationFunction* func, BNHighLevelILFunction* il, size_t exprIndex,
+ BNDisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement, size_t* count);
+ BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLanguageRepresentationFunctionLinearLines(
+ BNLanguageRepresentationFunction* func, BNHighLevelILFunction* il, size_t exprIndex,
+ BNDisassemblySettings* settings, bool asFullAst, size_t* count);
+ BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLanguageRepresentationFunctionBlockLines(
+ BNLanguageRepresentationFunction* func, BNBasicBlock* block, BNDisassemblySettings* settings, size_t* count);
+ BINARYNINJACOREAPI BNHighlightColor BNGetLanguageRepresentationFunctionHighlight(
+ BNLanguageRepresentationFunction* func, BNBasicBlock* block);
+ BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionCommentStartString(
+ BNLanguageRepresentationFunction* func);
+ BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionCommentEndString(
+ BNLanguageRepresentationFunction* func);
+ BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionAnnotationStartString(
+ BNLanguageRepresentationFunction* func);
+ BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionAnnotationEndString(
+ BNLanguageRepresentationFunction* func);
// Types
BINARYNINJACOREAPI bool BNTypesEqual(BNType* a, BNType* b);
@@ -5996,6 +6156,7 @@ extern "C"
BINARYNINJACOREAPI BNReferenceType BNTypeGetReferenceType(BNType* type);
BINARYNINJACOREAPI BNPointerBaseType BNTypeGetPointerBaseType(BNType* type);
BINARYNINJACOREAPI int64_t BNTypeGetPointerBaseOffset(BNType* type);
+ BINARYNINJACOREAPI BNNameType BNTypeGetNameType(BNType* type);
BINARYNINJACOREAPI char* BNGetTypeAlternateName(BNType* type);
BINARYNINJACOREAPI uint32_t BNTypeGetSystemCallNumber(BNType* type);
BINARYNINJACOREAPI bool BNTypeIsSystemCall(BNType* type);
@@ -6003,6 +6164,7 @@ extern "C"
BINARYNINJACOREAPI char* BNGetTypePointerSuffixString(BNType* type);
BINARYNINJACOREAPI BNInstructionTextToken* BNGetTypePointerSuffixTokens(BNType* type, uint8_t baseConfidence, size_t* count);
BINARYNINJACOREAPI void BNFreePointerSuffixList(BNPointerSuffix* suffix, size_t count);
+ BINARYNINJACOREAPI bool BNTypeShouldDisplayReturnType(BNType* type);
BINARYNINJACOREAPI char* BNGetTypeString(BNType* type, BNPlatform* platform, BNTokenEscapingType escaping);
BINARYNINJACOREAPI char* BNGetTypeStringBeforeName(BNType* type, BNPlatform* platform, BNTokenEscapingType escaping);
@@ -6155,6 +6317,11 @@ extern "C"
BINARYNINJACOREAPI BNStructureVariant BNGetStructureType(BNStructure* s);
BINARYNINJACOREAPI BNBaseStructure* BNGetBaseStructuresForStructure(BNStructure* s, size_t* count);
BINARYNINJACOREAPI void BNFreeBaseStructureList(BNBaseStructure* bases, size_t count);
+ BINARYNINJACOREAPI bool BNResolveStructureMemberOrBaseMember(BNStructure* s, BNBinaryView* data, uint64_t offset,
+ size_t size, void* callbackContext,
+ void (*resolveFunc)(void* ctxt, BNNamedTypeReference* baseName, BNStructure* resolvedStruct, size_t memberIndex,
+ uint64_t structOffset, uint64_t adjustedOffset, BNStructureMember member),
+ bool memberIndexHintValid, size_t memberIndexHint);
BINARYNINJACOREAPI BNStructure* BNStructureWithReplacedStructure(
BNStructure* s, BNStructure* from, BNStructure* to);
@@ -7120,10 +7287,10 @@ extern "C"
void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, BNTypeContext* typeCtx, size_t ctxCount);
BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLinesForData(void* ctxt, BNBinaryView* view, uint64_t addr,
BNType* type, const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count,
- BNTypeContext* typeCtx, size_t ctxCount);
+ BNTypeContext* typeCtx, size_t ctxCount, const char* language);
BINARYNINJACOREAPI BNDisassemblyTextLine* BNRenderLinesForData(BNBinaryView* data, uint64_t addr, BNType* type,
const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, BNTypeContext* typeCtx,
- size_t ctxCount);
+ size_t ctxCount, const char* language);
BINARYNINJACOREAPI void BNFreeDataRenderer(BNDataRenderer* renderer);
BINARYNINJACOREAPI BNDataRendererContainer* BNGetDataRendererContainer(void);
BINARYNINJACOREAPI void BNRegisterGenericDataRenderer(BNDataRendererContainer* container, BNDataRenderer* renderer);
@@ -7682,6 +7849,66 @@ extern "C"
BINARYNINJACOREAPI bool BNAnalysisMergeConflictSplitterCanSplit(BNAnalysisMergeConflictSplitter* splitter, const char* key, BNAnalysisMergeConflict* conflict);
BINARYNINJACOREAPI bool BNAnalysisMergeConflictSplitterSplit(BNAnalysisMergeConflictSplitter* splitter, const char* originalKey, BNAnalysisMergeConflict* originalConflict, BNKeyValueStore* result, char*** newKeys, BNAnalysisMergeConflict*** newConflicts, size_t* newCount);
+ // High level token emitter
+ BINARYNINJACOREAPI BNHighLevelILTokenEmitter* BNNewHighLevelILTokenEmitterReference(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNFreeHighLevelILTokenEmitter(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppend(BNHighLevelILTokenEmitter* emitter, BNInstructionTextToken* token);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterNewLine(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterIncreaseIndent(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterDecreaseIndent(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterScopeSeparator(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterBeginScope(BNHighLevelILTokenEmitter* emitter, BNScopeType type);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterEndScope(BNHighLevelILTokenEmitter* emitter, BNScopeType type);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterScopeContinuation(BNHighLevelILTokenEmitter* emitter, bool forceSameLine);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterFinalizeScope(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterNoIndentForThisLine(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterBeginForceZeroConfidence(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterEndForceZeroConfidence(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI BNTokenEmitterExpr BNHighLevelILTokenEmitterSetCurrentExpr(BNHighLevelILTokenEmitter* emitter, BNTokenEmitterExpr expr);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterRestoreCurrentExpr(BNHighLevelILTokenEmitter* emitter, BNTokenEmitterExpr expr);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterFinalize(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendOpenParen(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendCloseParen(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendOpenBracket(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendCloseBracket(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendOpenBrace(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendCloseBrace(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendSemicolon(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetBraceRequirement(
+ BNHighLevelILTokenEmitter* emitter, BNBraceRequirement required);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(BNHighLevelILTokenEmitter* emitter, bool braces);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(BNHighLevelILTokenEmitter* emitter, bool sameLine);
+ BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetSimpleScopeAllowed(BNHighLevelILTokenEmitter* emitter, bool allowed);
+ BINARYNINJACOREAPI BNBraceRequirement BNHighLevelILTokenEmitterGetBraceRequirement(
+ BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterIsSimpleScopeAllowed(BNHighLevelILTokenEmitter* emitter);
+ BINARYNINJACOREAPI BNInstructionTextToken* BNHighLevelILTokenEmitterGetCurrentTokens(BNHighLevelILTokenEmitter* emitter, size_t* tokenCount);
+ BINARYNINJACOREAPI BNDisassemblyTextLine* BNHighLevelILTokenEmitterGetLines(BNHighLevelILTokenEmitter* emitter, size_t* count);
+
+ BINARYNINJACOREAPI void BNAddHighLevelILSizeToken(size_t size, BNInstructionTextTokenType type, BNHighLevelILTokenEmitter* tokens);
+ BINARYNINJACOREAPI void BNAddHighLevelILFloatSizeToken(size_t size, BNInstructionTextTokenType type, BNHighLevelILTokenEmitter* tokens);
+ BINARYNINJACOREAPI void BNAddHighLevelILVarTextToken(BNHighLevelILFunction* func, const BNVariable* var,
+ BNHighLevelILTokenEmitter* tokens, size_t exprIndex, size_t size);
+ BINARYNINJACOREAPI void BNAddHighLevelILIntegerTextToken(BNHighLevelILFunction* func, size_t exprIndex,
+ int64_t val, size_t size, BNHighLevelILTokenEmitter* tokens);
+ BINARYNINJACOREAPI void BNAddHighLevelILArrayIndexToken(BNHighLevelILFunction* func, size_t exprIndex, int64_t val,
+ size_t size, BNHighLevelILTokenEmitter* tokens, uint64_t address);
+ BINARYNINJACOREAPI BNSymbolDisplayResult BNAddHighLevelILPointerTextToken(BNHighLevelILFunction* func,
+ size_t exprIndex, int64_t val, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings,
+ BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence, bool allowShortString);
+ BINARYNINJACOREAPI void BNAddHighLevelILConstantTextToken(BNHighLevelILFunction* func, size_t exprIndex,
+ int64_t val, size_t size, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings,
+ BNOperatorPrecedence precedence);
+ BINARYNINJACOREAPI char** BNAddNamesForOuterStructureMembers(BNBinaryView* data, BNType* type,
+ BNHighLevelILFunction* hlil, size_t varExpr, size_t* nameCount);
+
+ BINARYNINJACOREAPI int64_t BNZeroExtend(int64_t value, size_t sourceSize, size_t destSize);
+ BINARYNINJACOREAPI int64_t BNSignExtend(int64_t value, size_t sourceSize, size_t destSize);
+ BINARYNINJACOREAPI int64_t BNMaskToSize(int64_t value, size_t size);
+ BINARYNINJACOREAPI int64_t BNGetMaskForSize(size_t size);
+
#ifdef __cplusplus
}
#endif
diff --git a/binaryview.cpp b/binaryview.cpp
index 510d74a5..d2dbee56 100644
--- a/binaryview.cpp
+++ b/binaryview.cpp
@@ -4533,15 +4533,15 @@ bool BinaryView::FindNextData(uint64_t start, const DataBuffer& data, uint64_t&
}
bool BinaryView::FindNextText(uint64_t start, const std::string& data, uint64_t& result,
- Ref<DisassemblySettings> settings, BNFindFlag flags, BNFunctionGraphType graph)
+ Ref<DisassemblySettings> settings, BNFindFlag flags, const FunctionViewType& viewType)
{
- return BNFindNextText(m_object, start, data.c_str(), &result, settings->GetObject(), flags, graph);
+ return BNFindNextText(m_object, start, data.c_str(), &result, settings->GetObject(), flags, viewType.ToAPIObject());
}
bool BinaryView::FindNextConstant(
- uint64_t start, uint64_t constant, uint64_t& result, Ref<DisassemblySettings> settings, BNFunctionGraphType graph)
+ uint64_t start, uint64_t constant, uint64_t& result, Ref<DisassemblySettings> settings, const FunctionViewType& viewType)
{
- return BNFindNextConstant(m_object, start, constant, &result, settings->GetObject(), graph);
+ return BNFindNextConstant(m_object, start, constant, &result, settings->GetObject(), viewType.ToAPIObject());
}
@@ -4603,24 +4603,24 @@ bool BinaryView::FindNextData(uint64_t start, uint64_t end, const DataBuffer& da
bool BinaryView::FindNextText(uint64_t start, uint64_t end, const std::string& data, uint64_t& addr,
- Ref<DisassemblySettings> settings, BNFindFlag flags, BNFunctionGraphType graph,
+ Ref<DisassemblySettings> settings, BNFindFlag flags, const FunctionViewType& viewType,
const std::function<bool(size_t current, size_t total)>& progress)
{
ProgressContext fp;
fp.callback = progress;
return BNFindNextTextWithProgress(
- m_object, start, end, data.c_str(), &addr, settings->GetObject(), flags, graph, &fp, ProgressCallback);
+ m_object, start, end, data.c_str(), &addr, settings->GetObject(), flags, viewType.ToAPIObject(), &fp, ProgressCallback);
}
bool BinaryView::FindNextConstant(uint64_t start, uint64_t end, uint64_t constant, uint64_t& addr,
- Ref<DisassemblySettings> settings, BNFunctionGraphType graph,
+ Ref<DisassemblySettings> settings, const FunctionViewType& viewType,
const std::function<bool(size_t current, size_t total)>& progress)
{
ProgressContext fp;
fp.callback = progress;
return BNFindNextConstantWithProgress(
- m_object, start, end, constant, &addr, settings->GetObject(), graph, &fp, ProgressCallback);
+ m_object, start, end, constant, &addr, settings->GetObject(), viewType.ToAPIObject(), &fp, ProgressCallback);
}
@@ -4638,7 +4638,7 @@ bool BinaryView::FindAllData(uint64_t start, uint64_t end, const DataBuffer& dat
bool BinaryView::FindAllText(uint64_t start, uint64_t end, const std::string& data, Ref<DisassemblySettings> settings,
- BNFindFlag flags, BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& progress,
+ BNFindFlag flags, const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress,
const std::function<bool(uint64_t addr, const std::string& match, const LinearDisassemblyLine& line)>&
matchCallback)
{
@@ -4646,20 +4646,20 @@ bool BinaryView::FindAllText(uint64_t start, uint64_t end, const std::string& da
fp.callback = progress;
MatchCallbackContextForText mc;
mc.func = matchCallback;
- return BNFindAllTextWithProgress(m_object, start, end, data.c_str(), settings->GetObject(), flags, graph, &fp,
+ return BNFindAllTextWithProgress(m_object, start, end, data.c_str(), settings->GetObject(), flags, viewType.ToAPIObject(), &fp,
ProgressCallback, &mc, MatchCallbackForText);
}
bool BinaryView::FindAllConstant(uint64_t start, uint64_t end, uint64_t constant, Ref<DisassemblySettings> settings,
- BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& progress,
+ const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress,
const std::function<bool(uint64_t addr, const LinearDisassemblyLine& line)>& matchCallback)
{
ProgressContext fp;
fp.callback = progress;
MatchCallbackContextForConstant mc;
mc.func = matchCallback;
- return BNFindAllConstantWithProgress(m_object, start, end, constant, settings->GetObject(), graph, &fp,
+ return BNFindAllConstantWithProgress(m_object, start, end, constant, settings->GetObject(), viewType.ToAPIObject(), &fp,
ProgressCallback, &mc, MatchCallbackForConstant);
}
@@ -5316,6 +5316,20 @@ void BinaryView::SetUserGlobalPointerValue(const Confidence<RegisterValue>& valu
}
+optional<pair<string, BNStringType>> BinaryView::StringifyUnicodeData(Architecture* arch,
+ const DataBuffer& buffer, bool allowShortStrings)
+{
+ char* str = nullptr;
+ BNStringType type = AsciiString;
+ if (!BNStringifyUnicodeData(m_object, arch ? arch->GetObject() : nullptr, buffer.GetBufferObject(),
+ allowShortStrings, &str, &type))
+ return nullopt;
+
+ string result(str);
+ BNFreeString(str);
+ return make_pair(result, type);
+}
+
Relocation::Relocation(BNRelocation* reloc)
{
diff --git a/databuffer.cpp b/databuffer.cpp
index 6f9c3258..f8f15bd4 100644
--- a/databuffer.cpp
+++ b/databuffer.cpp
@@ -190,9 +190,9 @@ const uint8_t& DataBuffer::operator[](size_t offset) const
}
-string DataBuffer::ToEscapedString(bool nullTerminates) const
+string DataBuffer::ToEscapedString(bool nullTerminates, bool escapePrintable) const
{
- char* str = BNDataBufferToEscapedString(m_buffer, nullTerminates);
+ char* str = BNDataBufferToEscapedString(m_buffer, nullTerminates, escapePrintable);
string result = str;
BNFreeString(str);
return result;
diff --git a/datarenderer.cpp b/datarenderer.cpp
index a2ed1dae..4e4a3346 100644
--- a/datarenderer.cpp
+++ b/datarenderer.cpp
@@ -64,7 +64,7 @@ bool DataRenderer::IsValidForDataCallback(
BNDisassemblyTextLine* DataRenderer::GetLinesForDataCallback(void* ctxt, BNBinaryView* view, uint64_t addr,
BNType* type, const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count,
- BNTypeContext* typeCtx, size_t ctxCount)
+ BNTypeContext* typeCtx, size_t ctxCount, const char* language)
{
CallbackRef<DataRenderer> renderer(ctxt);
Ref<BinaryView> viewObj = new BinaryView(BNNewViewReference(view));
@@ -80,7 +80,8 @@ BNDisassemblyTextLine* DataRenderer::GetLinesForDataCallback(void* ctxt, BNBinar
contextType->AddRef();
context.push_back({contextType, typeCtx[i].offset});
}
- auto lines = renderer->GetLinesForData(viewObj, addr, typeObj, prefixes, width, context);
+ auto lines = renderer->GetLinesForData(viewObj, addr, typeObj, prefixes, width, context,
+ language ? language : string());
*count = lines.size();
BNDisassemblyTextLine* buf = new BNDisassemblyTextLine[lines.size()];
for (size_t i = 0; i < lines.size(); i++)
@@ -128,7 +129,7 @@ bool DataRenderer::IsValidForData(BinaryView* data, uint64_t addr, Type* type, v
vector<DisassemblyTextLine> DataRenderer::GetLinesForData(BinaryView* data, uint64_t addr, Type* type,
- const std::vector<InstructionTextToken>& prefix, size_t width, vector<pair<Type*, size_t>>& context)
+ const std::vector<InstructionTextToken>& prefix, size_t width, vector<pair<Type*, size_t>>& context, const string& language)
{
BNInstructionTextToken* prefixes = InstructionTextToken::CreateInstructionTextTokenList(prefix);
BNTypeContext* typeCtx = new BNTypeContext[context.size()];
@@ -139,7 +140,7 @@ vector<DisassemblyTextLine> DataRenderer::GetLinesForData(BinaryView* data, uint
}
size_t count = 0;
BNDisassemblyTextLine* lines = BNGetLinesForData(m_object, data->GetObject(), addr, type->GetObject(), prefixes,
- prefix.size(), width, &count, typeCtx, context.size());
+ prefix.size(), width, &count, typeCtx, context.size(), language.c_str());
delete[] typeCtx;
for (size_t i = 0; i < prefix.size(); i++)
@@ -168,7 +169,7 @@ vector<DisassemblyTextLine> DataRenderer::GetLinesForData(BinaryView* data, uint
vector<DisassemblyTextLine> DataRenderer::RenderLinesForData(BinaryView* data, uint64_t addr, Type* type,
- const std::vector<InstructionTextToken>& prefix, size_t width, vector<pair<Type*, size_t>>& context)
+ const std::vector<InstructionTextToken>& prefix, size_t width, vector<pair<Type*, size_t>>& context, const string& language)
{
BNInstructionTextToken* prefixes = InstructionTextToken::CreateInstructionTextTokenList(prefix);
BNTypeContext* typeCtx = new BNTypeContext[context.size()];
@@ -179,7 +180,8 @@ vector<DisassemblyTextLine> DataRenderer::RenderLinesForData(BinaryView* data, u
}
size_t count = 0;
BNDisassemblyTextLine* lines = BNRenderLinesForData(
- data->GetObject(), addr, type->GetObject(), prefixes, prefix.size(), width, &count, typeCtx, context.size());
+ data->GetObject(), addr, type->GetObject(), prefixes, prefix.size(), width, &count, typeCtx, context.size(),
+ language.c_str());
delete[] typeCtx;
for (size_t i = 0; i < prefix.size(); i++)
diff --git a/function.cpp b/function.cpp
index 033a8453..33cb5e52 100644
--- a/function.cpp
+++ b/function.cpp
@@ -200,12 +200,12 @@ ConstantData::ConstantData(BNRegisterValueType _state, uint64_t _value, size_t _
}
-DataBuffer ConstantData::ToDataBuffer() const
+pair<DataBuffer, BNBuiltinType> ConstantData::ToDataBuffer() const
{
if (func)
return func->GetConstantData(state, value, size);
- return DataBuffer();
+ return make_pair(DataBuffer(), BuiltinNone);
}
@@ -612,9 +612,11 @@ void PossibleValueSet::FreeAPIObject(BNPossibleValueSet* value)
}
-DataBuffer Function::GetConstantData(BNRegisterValueType state, uint64_t value, size_t size)
+pair<DataBuffer, BNBuiltinType> Function::GetConstantData(BNRegisterValueType state, uint64_t value, size_t size)
{
- return DataBuffer(BNGetConstantData(m_object, state, value, size));
+ BNBuiltinType builtin;
+ auto buffer = DataBuffer(BNGetConstantData(m_object, state, value, size, &builtin));
+ return make_pair(buffer, builtin);
}
@@ -908,21 +910,22 @@ Ref<HighLevelILFunction> Function::GetHighLevelILIfAvailable() const
}
-Ref<LanguageRepresentationFunction> Function::GetLanguageRepresentation() const
+Ref<LanguageRepresentationFunction> Function::GetLanguageRepresentation(const string& language) const
{
- BNLanguageRepresentationFunction* function = BNGetFunctionLanguageRepresentation(m_object);
+ BNLanguageRepresentationFunction* function = BNGetFunctionLanguageRepresentation(m_object, language.c_str());
if (!function)
return nullptr;
- return new LanguageRepresentationFunction(function);
+ return new CoreLanguageRepresentationFunction(function);
}
-Ref<LanguageRepresentationFunction> Function::GetLanguageRepresentationIfAvailable() const
+Ref<LanguageRepresentationFunction> Function::GetLanguageRepresentationIfAvailable(const string& language) const
{
- BNLanguageRepresentationFunction* function = BNGetFunctionLanguageRepresentationIfAvailable(m_object);
+ BNLanguageRepresentationFunction* function =
+ BNGetFunctionLanguageRepresentationIfAvailable(m_object, language.c_str());
if (!function)
return nullptr;
- return new LanguageRepresentationFunction(function);
+ return new CoreLanguageRepresentationFunction(function);
}
@@ -1275,9 +1278,9 @@ void Function::ApplyAutoDiscoveredType(Type* type)
}
-Ref<FlowGraph> Function::CreateFunctionGraph(BNFunctionGraphType type, DisassemblySettings* settings)
+Ref<FlowGraph> Function::CreateFunctionGraph(const FunctionViewType& type, DisassemblySettings* settings)
{
- BNFlowGraph* graph = BNCreateFunctionGraph(m_object, type, settings ? settings->GetObject() : nullptr);
+ BNFlowGraph* graph = BNCreateFunctionGraph(m_object, type.ToAPIObject(), settings ? settings->GetObject() : nullptr);
return new CoreFlowGraph(graph);
}
diff --git a/highlevelil.cpp b/highlevelil.cpp
index 7ac30f98..698b6119 100644
--- a/highlevelil.cpp
+++ b/highlevelil.cpp
@@ -341,6 +341,18 @@ bool HighLevelILFunction::IsVarLiveAt(const Variable& var, const size_t instr) c
}
+bool HighLevelILFunction::HasSideEffects(const HighLevelILInstruction& instr)
+{
+ return BNHighLevelILHasSideEffects(instr.function->GetObject(), instr.exprIndex);
+}
+
+
+BNScopeType HighLevelILFunction::GetExprScopeType(const HighLevelILInstruction& instr)
+{
+ return BNGetHighLevelILExprScopeType(instr.function->GetObject(), instr.exprIndex);
+}
+
+
set<size_t> HighLevelILFunction::GetVariableSSAVersions(const Variable& var) const
{
size_t count;
@@ -568,3 +580,332 @@ set<size_t> HighLevelILFunction::GetUsesForLabel(uint64_t label)
BNFreeILInstructionList(uses);
return result;
}
+
+
+set<Variable> HighLevelILFunction::GetVariables()
+{
+ size_t count;
+ BNVariable* vars = BNGetHighLevelILVariables(m_object, &count);
+
+ set<Variable> result;
+ for (size_t i = 0; i < count; ++i)
+ result.emplace(vars[i]);
+
+ BNFreeVariableList(vars);
+ return result;
+}
+
+
+set<Variable> HighLevelILFunction::GetAliasedVariables()
+{
+ size_t count;
+ BNVariable* vars = BNGetHighLevelILAliasedVariables(m_object, &count);
+
+ set<Variable> result;
+ for (size_t i = 0; i < count; ++i)
+ result.emplace(vars[i]);
+
+ BNFreeVariableList(vars);
+ return result;
+}
+
+
+set<SSAVariable> HighLevelILFunction::GetSSAVariables()
+{
+ size_t count;
+ BNVariable* vars = BNGetHighLevelILVariables(m_object, &count);
+
+ set<SSAVariable> result;
+ for (size_t i = 0; i < count; ++i)
+ {
+ size_t versionCount;
+ size_t* versions = BNGetHighLevelILVariableSSAVersions(m_object, &vars[i], &versionCount);
+ for (size_t j = 0; j < versionCount; ++j)
+ result.emplace(vars[i], versions[j]);
+ BNFreeILInstructionList(versions);
+ }
+
+ BNFreeVariableList(vars);
+ return result;
+}
+
+
+HighLevelILTokenEmitter::CurrentExprGuard::CurrentExprGuard(HighLevelILTokenEmitter& parent, const BNTokenEmitterExpr& expr):
+ m_parent(&parent)
+{
+ m_expr = BNHighLevelILTokenEmitterSetCurrentExpr(m_parent->m_object, expr);
+}
+
+
+HighLevelILTokenEmitter::CurrentExprGuard::~CurrentExprGuard()
+{
+ BNHighLevelILTokenEmitterRestoreCurrentExpr(m_parent->m_object, m_expr);
+}
+
+
+HighLevelILTokenEmitter::HighLevelILTokenEmitter(BNHighLevelILTokenEmitter* emitter)
+{
+ m_object = emitter;
+}
+
+
+void HighLevelILTokenEmitter::NewLine()
+{
+ BNHighLevelILTokenEmitterNewLine(m_object);
+}
+
+
+void HighLevelILTokenEmitter::IncreaseIndent()
+{
+ BNHighLevelILTokenEmitterIncreaseIndent(m_object);
+}
+
+
+void HighLevelILTokenEmitter::DecreaseIndent()
+{
+ BNHighLevelILTokenEmitterDecreaseIndent(m_object);
+}
+
+
+void HighLevelILTokenEmitter::ScopeSeparator()
+{
+ BNHighLevelILTokenEmitterScopeSeparator(m_object);
+}
+
+
+void HighLevelILTokenEmitter::BeginScope(BNScopeType scopeType)
+{
+ BNHighLevelILTokenEmitterBeginScope(m_object, scopeType);
+}
+
+
+void HighLevelILTokenEmitter::EndScope(BNScopeType scopeType)
+{
+ BNHighLevelILTokenEmitterEndScope(m_object, scopeType);
+}
+
+
+void HighLevelILTokenEmitter::ScopeContinuation(bool forceSameLine)
+{
+ BNHighLevelILTokenEmitterScopeContinuation(m_object, forceSameLine);
+}
+
+
+void HighLevelILTokenEmitter::FinalizeScope()
+{
+ BNHighLevelILTokenEmitterFinalizeScope(m_object);
+}
+
+
+void HighLevelILTokenEmitter::NoIndentForThisLine()
+{
+ BNHighLevelILTokenEmitterNoIndentForThisLine(m_object);
+}
+
+
+void HighLevelILTokenEmitter::BeginForceZeroConfidence()
+{
+ BNHighLevelILTokenEmitterBeginForceZeroConfidence(m_object);
+}
+
+
+void HighLevelILTokenEmitter::EndForceZeroConfidence()
+{
+ BNHighLevelILTokenEmitterEndForceZeroConfidence(m_object);
+}
+
+
+HighLevelILTokenEmitter::CurrentExprGuard HighLevelILTokenEmitter::SetCurrentExpr(const HighLevelILInstruction& expr)
+{
+ return CurrentExprGuard(*this, {expr.address, expr.sourceOperand, expr.exprIndex, expr.instructionIndex});
+}
+
+
+void HighLevelILTokenEmitter::Finalize()
+{
+ BNHighLevelILTokenEmitterFinalize(m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendOpenParen()
+{
+ BNHighLevelILTokenEmitterAppendOpenParen(m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendCloseParen()
+{
+ BNHighLevelILTokenEmitterAppendCloseParen(m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendOpenBracket()
+{
+ BNHighLevelILTokenEmitterAppendOpenBracket(m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendCloseBracket()
+{
+ BNHighLevelILTokenEmitterAppendCloseBracket(m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendOpenBrace()
+{
+ BNHighLevelILTokenEmitterAppendOpenBrace(m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendCloseBrace()
+{
+ BNHighLevelILTokenEmitterAppendCloseBrace(m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendSemicolon()
+{
+ BNHighLevelILTokenEmitterAppendSemicolon(m_object);
+}
+
+
+vector<InstructionTextToken> HighLevelILTokenEmitter::GetCurrentTokens() const
+{
+ size_t count = 0;
+ BNInstructionTextToken* tokens = BNHighLevelILTokenEmitterGetCurrentTokens(m_object, &count);
+ return InstructionTextToken::ConvertAndFreeInstructionTextTokenList(tokens, count);
+}
+
+
+void HighLevelILTokenEmitter::SetBraceRequirement(BNBraceRequirement required)
+{
+ BNHighLevelILTokenEmitterSetBraceRequirement(m_object, required);
+}
+
+
+void HighLevelILTokenEmitter::SetBracesAroundSwitchCases(bool braces)
+{
+ BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(m_object, braces);
+}
+
+
+void HighLevelILTokenEmitter::SetDefaultBracesOnSameLine(bool sameLine)
+{
+ BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(m_object, sameLine);
+}
+
+
+void HighLevelILTokenEmitter::SetSimpleScopeAllowed(bool allowed)
+{
+ BNHighLevelILTokenEmitterSetSimpleScopeAllowed(m_object, allowed);
+}
+
+
+BNBraceRequirement HighLevelILTokenEmitter::GetBraceRequirement() const
+{
+ return BNHighLevelILTokenEmitterGetBraceRequirement(m_object);
+}
+
+
+bool HighLevelILTokenEmitter::HasBracesAroundSwitchCases() const
+{
+ return BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(m_object);
+}
+
+
+bool HighLevelILTokenEmitter::GetDefaultBracesOnSameLine() const
+{
+ return BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(m_object);
+}
+
+
+bool HighLevelILTokenEmitter::IsSimpleScopeAllowed() const
+{
+ return BNHighLevelILTokenEmitterIsSimpleScopeAllowed(m_object);
+}
+
+
+vector<DisassemblyTextLine> HighLevelILTokenEmitter::GetLines() const
+{
+ size_t count = 0;
+ BNDisassemblyTextLine* lines = BNHighLevelILTokenEmitterGetLines(m_object, &count);
+
+ vector<DisassemblyTextLine> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ {
+ DisassemblyTextLine line;
+ line.addr = lines[i].addr;
+ line.instrIndex = lines[i].instrIndex;
+ line.highlight = lines[i].highlight;
+ line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count);
+ line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount);
+ result.push_back(line);
+ }
+
+ BNFreeDisassemblyTextLines(lines, count);
+ return result;
+}
+
+
+void HighLevelILTokenEmitter::AppendSizeToken(size_t size, BNInstructionTextTokenType type)
+{
+ BNAddHighLevelILSizeToken(size, type, m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendFloatSizeToken(size_t size, BNInstructionTextTokenType type)
+{
+ BNAddHighLevelILFloatSizeToken(size, type, m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendVarTextToken(const Variable& var, const HighLevelILInstruction& instr, size_t size)
+{
+ BNAddHighLevelILVarTextToken(instr.function->GetObject(), &var, m_object, instr.exprIndex, size);
+}
+
+
+void HighLevelILTokenEmitter::AppendIntegerTextToken(const HighLevelILInstruction& instr, int64_t val, size_t size)
+{
+ BNAddHighLevelILIntegerTextToken(instr.function->GetObject(), instr.exprIndex, val, size, m_object);
+}
+
+
+void HighLevelILTokenEmitter::AppendArrayIndexToken(
+ const HighLevelILInstruction& instr, int64_t val, size_t size, uint64_t address)
+{
+ BNAddHighLevelILArrayIndexToken(instr.function->GetObject(), instr.exprIndex, val, size, m_object, address);
+}
+
+
+BNSymbolDisplayResult HighLevelILTokenEmitter::AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val,
+ DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence,
+ bool allowShortString)
+{
+ return BNAddHighLevelILPointerTextToken(instr.function->GetObject(), instr.exprIndex, val, m_object,
+ settings ? settings->GetObject() : nullptr, symbolDisplay, precedence, allowShortString);
+}
+
+
+void HighLevelILTokenEmitter::AppendConstantTextToken(const HighLevelILInstruction& instr, int64_t val, size_t size,
+ DisassemblySettings* settings, BNOperatorPrecedence precedence)
+{
+ BNAddHighLevelILConstantTextToken(instr.function->GetObject(), instr.exprIndex, val, size, m_object,
+ settings ? settings->GetObject() : nullptr, precedence);
+}
+
+
+void HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ BinaryView* data, Type* type, const HighLevelILInstruction& var, vector<string>& nameList)
+{
+ size_t nameCount = 0;
+ char** names = BNAddNamesForOuterStructureMembers(
+ data->GetObject(), type->GetObject(), var.function->GetObject(), var.exprIndex, &nameCount);
+ vector<string> newNames;
+ newNames.reserve(nameCount);
+ for (size_t i = 0; i < nameCount; i++)
+ newNames.push_back(names[i]);
+ BNFreeStringList(names, nameCount);
+ nameList.insert(nameList.begin(), newNames.begin(), newNames.end());
+}
diff --git a/lang/c/CMakeLists.txt b/lang/c/CMakeLists.txt
new file mode 100644
index 00000000..9ccc89f2
--- /dev/null
+++ b/lang/c/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(lang_pseudoc)
+
+if(NOT BN_INTERNAL_BUILD)
+ add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api)
+endif()
+
+file(GLOB SOURCES
+ *.cpp
+ *.h)
+
+if(DEMO)
+ add_library(lang_pseudoc STATIC ${SOURCES})
+else()
+ add_library(lang_pseudoc SHARED ${SOURCES})
+endif()
+
+target_include_directories(lang_pseudoc
+ PRIVATE ${PROJECT_SOURCE_DIR})
+
+if(WIN32)
+ target_link_directories(lang_pseudoc
+ PRIVATE ${BN_INSTALL_DIR})
+ target_link_libraries(lang_pseudoc binaryninjaapi binaryninjacore)
+else()
+ target_link_libraries(lang_pseudoc binaryninjaapi)
+endif()
+
+set_target_properties(lang_pseudoc PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+ C_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON)
+
+if(BN_INTERNAL_BUILD)
+ plugin_rpath(lang_pseudoc)
+ set_target_properties(lang_pseudoc PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+endif()
diff --git a/lang/c/pseudoc.cpp b/lang/c/pseudoc.cpp
new file mode 100644
index 00000000..b2dca522
--- /dev/null
+++ b/lang/c/pseudoc.cpp
@@ -0,0 +1,2852 @@
+#include <inttypes.h>
+#include "pseudoc.h"
+#include "highlevelilinstruction.h"
+
+using namespace std;
+using namespace BinaryNinja;
+
+
+PseudoCFunction::PseudoCFunction(
+ Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) :
+ LanguageRepresentationFunction(arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction)
+{
+}
+
+
+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);
+}
+
+
+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.AppendOpenBrace();
+ tokens.NewLine();
+ tokens.IncreaseIndent();
+ }
+}
+
+
+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.AppendCloseBrace();
+ }
+}
+
+
+BNSymbolDisplayResult PseudoCFunction::AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val,
+ vector<InstructionTextToken>& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence)
+{
+ Confidence<Ref<Type>> type = instr.GetType();
+ if (type && (type->GetClass() == PointerTypeClass) && type->IsConst())
+ {
+ string stringValue;
+ size_t childWidth = 0;
+ if (auto child = type->GetChildType(); child)
+ 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 && (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<BinaryView> data = GetFunction()->GetView();
+ vector<InstructionTextToken> 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)
+{
+ char sizeStr[32];
+
+ switch (size)
+ {
+ case 0:
+ return {};
+ case 1:
+ return (isSigned ? "int8_t" : "uint8_t");
+ case 2:
+ return (isSigned ? "int16_t" : "uint16_t");
+ case 4:
+ return (isSigned ? "int32_t" : "uint32_t");
+ case 8:
+ return (isSigned ? "int64_t" : "uint64_t");
+ case 10:
+ return (isSigned ? "int80_t" : "uint80_t");
+ case 16:
+ return (isSigned ? "int128_t" : "uint128_t");
+ }
+
+ snprintf(sizeStr, sizeof(sizeStr), "%sint%" PRIuPTR "_t", isSigned ? "" : "u", size);
+ return {sizeStr};
+}
+
+
+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, bool asFullAst, BNOperatorPrecedence precedence,
+ std::optional<bool> signedHint)
+{
+ const auto leftExpr = instr.GetLeftExpr();
+ const auto rightExpr = instr.GetRightExpr();
+
+ GetExprTextInternal(leftExpr, emitter, settings, asFullAst, precedence, false, signedHint);
+ emitter.Append(OperationToken, comparison);
+ GetExprTextInternal(rightExpr, emitter, settings, asFullAst, precedence, false, signedHint);
+}
+
+
+void PseudoCFunction::AppendTwoOperand(const string& operand, const HighLevelILInstruction& instr,
+ HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence,
+ std::optional<bool> 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();
+ GetExprTextInternal(high, emitter, settings, asFullAst);
+ emitter.Append(TextToken, ", ");
+ GetExprTextInternal(low, emitter, settings, asFullAst);
+ emitter.AppendCloseParen();
+ }
+
+
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ if (leftExpr.operation == HLIL_VAR && (operand == " + " || operand == " - "))
+ {
+ const auto variableType = GetFunction()->GetVariableType(leftExpr.GetVariable());
+ if (variableType)
+ {
+ const auto childType = variableType->GetChildType();
+ if (variableType->IsPointer() && childType && childType->GetWidth() != 1)
+ {
+ emitter.AppendOpenParen();
+ emitter.Append(TypeNameToken, "char");
+ emitter.Append(TextToken, "*");
+ emitter.AppendCloseParen();
+ }
+ }
+ }
+ }
+
+ GetExprTextInternal(leftExpr, emitter, settings, asFullAst, 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<HLIL_CONST>(), 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<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size);
+ return;
+ }
+ if ((operand == " - ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), 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<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size);
+ return;
+ }
+
+ emitter.Append(OperationToken, operand);
+ GetExprTextInternal(rightExpr, emitter, settings, asFullAst, 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<Type> PseudoCFunction::GetFieldType(const HighLevelILInstruction& var, bool deref)
+{
+ Ref<Type> 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> type, uint64_t offset, size_t memberIndex, bool deref)
+{
+ if (type && (type->GetClass() == StructureTypeClass))
+ {
+ std::optional<size_t> 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 FieldDisplayMemberOffset;
+ else
+ return FieldDisplayNone;
+}
+
+
+void PseudoCFunction::AppendFieldTextTokens(const HighLevelILInstruction& var, uint64_t offset,
+ size_t memberIndex, size_t size, HighLevelILTokenEmitter& tokens, bool deref, bool displayDeref)
+{
+ const auto type = GetFieldType(var, deref);
+ const auto fieldDisplayType = GetFieldDisplayType(type, offset, memberIndex, deref);
+ switch (fieldDisplayType)
+ {
+ case FieldDisplayName:
+ {
+ std::optional<size_t> memberIndexHint;
+ if (memberIndex != BN_INVALID_EXPR)
+ memberIndexHint = memberIndex;
+
+ if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0,
+ [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset,
+ uint64_t adjustedOffset, const StructureMember& member) {
+ if (deref && displayDeref)
+ tokens.Append(OperationToken, "->");
+ else
+ tokens.Append(OperationToken, ".");
+ deref = false;
+
+ vector<string> nameList {member.name};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, var, 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
+ if (deref && displayDeref)
+ tokens.Append(OperationToken, "->");
+ else
+ tokens.Append(OperationToken, ".");
+ char offsetStr[64];
+ snprintf(
+ offsetStr, sizeof(offsetStr), "__offset(0x%" PRIx64 ")%s", offset, Type::GetSizeSuffix(size).c_str());
+
+ vector<string> nameList {offsetStr};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(GetFunction()->GetView(), type, var, nameList);
+
+ tokens.Append(StructOffsetToken, offsetStr, offset, size, 0, BN_FULL_CONFIDENCE, nameList);
+ return;
+ }
+
+ case FieldDisplayOffset:
+ {
+ /* this is handled before the display */
+ return;
+ }
+
+ case FieldDisplayMemberOffset:
+ {
+ tokens.AppendOpenBracket();
+ tokens.AppendIntegerTextToken(var, offset, size);
+ tokens.AppendCloseBracket();
+ return;
+ }
+
+ default: break;
+ }
+}
+
+
+void PseudoCFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement)
+{
+ GetExprTextInternal(instr, tokens, settings, asFullAst, precedence, statement);
+}
+
+
+void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement,
+ optional<bool> 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())
+ {
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ for (auto& token: instr.GetType()->GetTokens(GetArchitecture()->GetStandalonePlatform()))
+ {
+ tokens.Append(token);
+ }
+ tokens.AppendCloseParen();
+ tokens.Append(TextToken, " ");
+ }
+ if (settings && settings->IsOptionSet(ShowILOpcodes))
+ {
+ tokens.Append(OperationToken, "/*");
+ switch (instr.operation)
+ {
+ case HLIL_NOP: tokens.Append(OperationToken, "HLIL_NOP"); break;
+ case HLIL_BLOCK: tokens.Append(OperationToken, "HLIL_BLOCK"); break;
+ case HLIL_IF: tokens.Append(OperationToken, "HLIL_IF"); break;
+ case HLIL_WHILE: tokens.Append(OperationToken, "HLIL_WHILE"); break;
+ case HLIL_DO_WHILE: tokens.Append(OperationToken, "HLIL_DO_WHILE"); break;
+ case HLIL_FOR: tokens.Append(OperationToken, "HLIL_FOR"); break;
+ case HLIL_SWITCH: tokens.Append(OperationToken, "HLIL_SWITCH"); break;
+ case HLIL_CASE: tokens.Append(OperationToken, "HLIL_CASE"); break;
+ case HLIL_BREAK: tokens.Append(OperationToken, "HLIL_BREAK"); break;
+ case HLIL_CONTINUE: tokens.Append(OperationToken, "HLIL_CONTINUE"); break;
+ case HLIL_JUMP: tokens.Append(OperationToken, "HLIL_JUMP"); break;
+ case HLIL_RET: tokens.Append(OperationToken, "HLIL_RET"); break;
+ case HLIL_NORET: tokens.Append(OperationToken, "HLIL_NORET"); break;
+ case HLIL_GOTO: tokens.Append(OperationToken, "HLIL_GOTO"); break;
+ case HLIL_LABEL: tokens.Append(OperationToken, "HLIL_LABEL"); break;
+ case HLIL_VAR_DECLARE: tokens.Append(OperationToken, "HLIL_VAR_DECLARE"); break;
+ case HLIL_VAR_INIT: tokens.Append(OperationToken, "HLIL_VAR_INIT"); break;
+ case HLIL_ASSIGN: tokens.Append(OperationToken, "HLIL_ASSIGN"); break;
+ case HLIL_ASSIGN_UNPACK: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK"); break;
+ case HLIL_VAR: tokens.Append(OperationToken, "HLIL_VAR"); break;
+ case HLIL_STRUCT_FIELD: tokens.Append(OperationToken, "HLIL_STRUCT_FIELD"); break;
+ case HLIL_ARRAY_INDEX: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX"); break;
+ case HLIL_SPLIT: tokens.Append(OperationToken, "HLIL_SPLIT"); break;
+ case HLIL_DEREF: tokens.Append(OperationToken, "HLIL_DEREF"); break;
+ case HLIL_DEREF_FIELD: tokens.Append(OperationToken, "HLIL_DEREF_FIELD"); break;
+ case HLIL_ADDRESS_OF: tokens.Append(OperationToken, "HLIL_ADDRESS_OF"); break;
+ case HLIL_CONST: tokens.Append(OperationToken, "HLIL_CONST"); break;
+ case HLIL_CONST_DATA: tokens.Append(OperationToken, "HLIL_CONST_DATA"); break;
+ case HLIL_CONST_PTR: tokens.Append(OperationToken, "HLIL_CONST_PTR"); break;
+ case HLIL_EXTERN_PTR: tokens.Append(OperationToken, "HLIL_EXTERN_PTR"); break;
+ case HLIL_FLOAT_CONST: tokens.Append(OperationToken, "HLIL_FLOAT_CONST"); break;
+ case HLIL_IMPORT: tokens.Append(OperationToken, "HLIL_IMPORT"); break;
+ case HLIL_ADD: tokens.Append(OperationToken, "HLIL_ADD"); break;
+ case HLIL_ADC: tokens.Append(OperationToken, "HLIL_ADC"); break;
+ case HLIL_SUB: tokens.Append(OperationToken, "HLIL_SUB"); break;
+ case HLIL_SBB: tokens.Append(OperationToken, "HLIL_SBB"); break;
+ case HLIL_AND: tokens.Append(OperationToken, "HLIL_AND"); break;
+ case HLIL_OR: tokens.Append(OperationToken, "HLIL_OR"); break;
+ case HLIL_XOR: tokens.Append(OperationToken, "HLIL_XOR"); break;
+ case HLIL_LSL: tokens.Append(OperationToken, "HLIL_LSL"); break;
+ case HLIL_LSR: tokens.Append(OperationToken, "HLIL_LSR"); break;
+ case HLIL_ASR: tokens.Append(OperationToken, "HLIL_ASR"); break;
+ case HLIL_ROL: tokens.Append(OperationToken, "HLIL_ROL"); break;
+ case HLIL_RLC: tokens.Append(OperationToken, "HLIL_RLC"); break;
+ case HLIL_ROR: tokens.Append(OperationToken, "HLIL_ROR"); break;
+ case HLIL_RRC: tokens.Append(OperationToken, "HLIL_RRC"); break;
+ case HLIL_MUL: tokens.Append(OperationToken, "HLIL_MUL"); break;
+ case HLIL_MULU_DP: tokens.Append(OperationToken, "HLIL_MULU_DP"); break;
+ case HLIL_MULS_DP: tokens.Append(OperationToken, "HLIL_MULS_DP"); break;
+ case HLIL_DIVU: tokens.Append(OperationToken, "HLIL_DIVU"); break;
+ case HLIL_DIVU_DP: tokens.Append(OperationToken, "HLIL_DIVU_DP"); break;
+ case HLIL_DIVS: tokens.Append(OperationToken, "HLIL_DIVS"); break;
+ case HLIL_DIVS_DP: tokens.Append(OperationToken, "HLIL_DIVS_DP"); break;
+ case HLIL_MODU: tokens.Append(OperationToken, "HLIL_MODU"); break;
+ case HLIL_MODU_DP: tokens.Append(OperationToken, "HLIL_MODU_DP"); break;
+ case HLIL_MODS: tokens.Append(OperationToken, "HLIL_MODS"); break;
+ case HLIL_MODS_DP: tokens.Append(OperationToken, "HLIL_MODS_DP"); break;
+ case HLIL_NEG: tokens.Append(OperationToken, "HLIL_NEG"); break;
+ case HLIL_NOT: tokens.Append(OperationToken, "HLIL_NOT"); break;
+ case HLIL_SX: tokens.Append(OperationToken, "HLIL_SX"); break;
+ case HLIL_ZX: tokens.Append(OperationToken, "HLIL_ZX"); break;
+ case HLIL_LOW_PART: tokens.Append(OperationToken, "HLIL_LOW_PART"); break;
+ case HLIL_CALL: tokens.Append(OperationToken, "HLIL_CALL"); break;
+ case HLIL_CMP_E: tokens.Append(OperationToken, "HLIL_CMP_E"); break;
+ case HLIL_CMP_NE: tokens.Append(OperationToken, "HLIL_CMP_NE"); break;
+ case HLIL_CMP_SLT: tokens.Append(OperationToken, "HLIL_CMP_SLT"); break;
+ case HLIL_CMP_ULT: tokens.Append(OperationToken, "HLIL_CMP_ULT"); break;
+ case HLIL_CMP_SLE: tokens.Append(OperationToken, "HLIL_CMP_SLE"); break;
+ case HLIL_CMP_ULE: tokens.Append(OperationToken, "HLIL_CMP_ULE"); break;
+ case HLIL_CMP_SGE: tokens.Append(OperationToken, "HLIL_CMP_SGE"); break;
+ case HLIL_CMP_UGE: tokens.Append(OperationToken, "HLIL_CMP_UGE"); break;
+ case HLIL_CMP_SGT: tokens.Append(OperationToken, "HLIL_CMP_SGT"); break;
+ case HLIL_CMP_UGT: tokens.Append(OperationToken, "HLIL_CMP_UGT"); break;
+ case HLIL_TEST_BIT: tokens.Append(OperationToken, "HLIL_TEST_BIT"); break;
+ case HLIL_BOOL_TO_INT: tokens.Append(OperationToken, "HLIL_BOOL_TO_INT"); break;
+ case HLIL_ADD_OVERFLOW: tokens.Append(OperationToken, "HLIL_ADD_OVERFLOW"); break;
+ case HLIL_SYSCALL: tokens.Append(OperationToken, "HLIL_SYSCALL"); break;
+ case HLIL_TAILCALL: tokens.Append(OperationToken, "HLIL_TAILCALL"); break;
+ case HLIL_INTRINSIC: tokens.Append(OperationToken, "HLIL_INTRINSIC"); break;
+ case HLIL_BP: tokens.Append(OperationToken, "HLIL_BP"); break;
+ case HLIL_TRAP: tokens.Append(OperationToken, "HLIL_TRAP"); break;
+ case HLIL_UNDEF: tokens.Append(OperationToken, "HLIL_UNDEF"); break;
+ case HLIL_UNIMPL: tokens.Append(OperationToken, "HLIL_UNIMPL"); break;
+ case HLIL_UNIMPL_MEM: tokens.Append(OperationToken, "HLIL_UNIMPL_MEM"); break;
+ case HLIL_FADD: tokens.Append(OperationToken, "HLIL_FADD"); break;
+ case HLIL_FSUB: tokens.Append(OperationToken, "HLIL_FSUB"); break;
+ case HLIL_FMUL: tokens.Append(OperationToken, "HLIL_FMUL"); break;
+ case HLIL_FDIV: tokens.Append(OperationToken, "HLIL_FDIV"); break;
+ case HLIL_FSQRT: tokens.Append(OperationToken, "HLIL_FSQRT"); break;
+ case HLIL_FNEG: tokens.Append(OperationToken, "HLIL_FNEG"); break;
+ case HLIL_FABS: tokens.Append(OperationToken, "HLIL_FABS"); break;
+ case HLIL_FLOAT_TO_INT: tokens.Append(OperationToken, "HLIL_FLOAT_TO_INT"); break;
+ case HLIL_INT_TO_FLOAT: tokens.Append(OperationToken, "HLIL_INT_TO_FLOAT"); break;
+ case HLIL_FLOAT_CONV: tokens.Append(OperationToken, "HLIL_FLOAT_CONV"); break;
+ case HLIL_ROUND_TO_INT: tokens.Append(OperationToken, "HLIL_ROUND_TO_INT"); break;
+ case HLIL_FLOOR: tokens.Append(OperationToken, "HLIL_FLOOR"); break;
+ case HLIL_CEIL: tokens.Append(OperationToken, "HLIL_CEIL"); break;
+ case HLIL_FTRUNC: tokens.Append(OperationToken, "HLIL_FTRUNC"); break;
+ case HLIL_FCMP_E: tokens.Append(OperationToken, "HLIL_FCMP_E"); break;
+ case HLIL_FCMP_NE: tokens.Append(OperationToken, "HLIL_FCMP_NE"); break;
+ case HLIL_FCMP_LT: tokens.Append(OperationToken, "HLIL_FCMP_LT"); break;
+ case HLIL_FCMP_LE: tokens.Append(OperationToken, "HLIL_FCMP_LE"); break;
+ case HLIL_FCMP_GE: tokens.Append(OperationToken, "HLIL_FCMP_GE"); break;
+ case HLIL_FCMP_GT: tokens.Append(OperationToken, "HLIL_FCMP_GT"); break;
+ case HLIL_FCMP_O: tokens.Append(OperationToken, "HLIL_FCMP_O"); break;
+ case HLIL_FCMP_UO: tokens.Append(OperationToken, "HLIL_FCMP_UO"); break;
+ case HLIL_UNREACHABLE: tokens.Append(OperationToken, "HLIL_UNREACHABLE"); break;
+ case HLIL_WHILE_SSA: tokens.Append(OperationToken, "HLIL_WHILE_SSA"); break;
+ case HLIL_DO_WHILE_SSA: tokens.Append(OperationToken, "HLIL_DO_WHILE_SSA"); break;
+ case HLIL_FOR_SSA: tokens.Append(OperationToken, "HLIL_FOR_SSA"); break;
+ case HLIL_VAR_INIT_SSA: tokens.Append(OperationToken, "HLIL_VAR_INIT_SSA"); break;
+ case HLIL_ASSIGN_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_MEM_SSA"); break;
+ case HLIL_ASSIGN_UNPACK_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK_MEM_SSA"); break;
+ case HLIL_VAR_SSA: tokens.Append(OperationToken, "HLIL_VAR_SSA"); break;
+ case HLIL_ARRAY_INDEX_SSA: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX_SSA"); break;
+ case HLIL_DEREF_SSA: tokens.Append(OperationToken, "HLIL_DEREF_SSA"); break;
+ case HLIL_DEREF_FIELD_SSA: tokens.Append(OperationToken, "HLIL_DEREF_FIELD_SSA"); break;
+ case HLIL_CALL_SSA: tokens.Append(OperationToken, "HLIL_CALL_SSA"); break;
+ case HLIL_SYSCALL_SSA: tokens.Append(OperationToken, "HLIL_SYSCALL_SSA"); break;
+ case HLIL_INTRINSIC_SSA: tokens.Append(OperationToken, "HLIL_INTRINSIC_SSA"); break;
+ case HLIL_VAR_PHI: tokens.Append(OperationToken, "HLIL_VAR_PHI"); break;
+ case HLIL_MEM_PHI: tokens.Append(OperationToken, "HLIL_MEM_PHI"); break;
+ }
+ tokens.Append(OperationToken, "*/");
+ tokens.Append(TextToken, " ");
+ }
+
+ switch (instr.operation)
+ {
+ case HLIL_BLOCK:
+ [&]() {
+ const auto exprs = instr.GetBlockExprs<HLIL_BLOCK>();
+ 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 (asFullAst && (instr.exprIndex == GetHighLevelILFunction()->GetRootExpr().exprIndex) && (exprs.size() > 1) &&
+ (next == exprs.end()) && ((*i).operation == HLIL_RET) &&
+ ((*i).GetSourceExprs<HLIL_RET>().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
+ GetExprTextInternal(*i, tokens, settings, true, TopLevelOperatorPrecedence, true);
+ tokens.NewLine();
+ }
+ }();
+ break;
+
+ case HLIL_FOR:
+ [&]() {
+ const auto initExpr = instr.GetInitExpr<HLIL_FOR>();
+ const auto condExpr = instr.GetConditionExpr<HLIL_FOR>();
+ const auto updateExpr = instr.GetUpdateExpr<HLIL_FOR>();
+ const auto loopExpr = instr.GetLoopExpr<HLIL_FOR>();
+
+ if (asFullAst)
+ {
+ tokens.Append(KeywordToken, "for ");
+ tokens.AppendOpenParen();
+ if (initExpr.operation != HLIL_NOP)
+ GetExprTextInternal(initExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, "; ");
+ if (condExpr.operation != HLIL_NOP)
+ GetExprTextInternal(condExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, "; ");
+ if (updateExpr.operation != HLIL_NOP)
+ GetExprTextInternal(updateExpr, tokens, settings, asFullAst);
+ tokens.AppendCloseParen();
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprTextInternal(loopExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+ }
+ else
+ {
+ tokens.Append(KeywordToken, "while ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(condExpr, tokens, settings);
+ tokens.AppendCloseParen();
+ }
+ }();
+ break;
+
+ case HLIL_IF:
+ [&]() {
+ const auto condExpr = instr.GetConditionExpr<HLIL_IF>();
+ const auto trueExpr = instr.GetTrueExpr<HLIL_IF>();
+ const auto falseExpr = instr.GetFalseExpr<HLIL_IF>();
+
+ tokens.Append(KeywordToken, "if ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(condExpr, tokens, settings, asFullAst);
+ tokens.AppendCloseParen();
+ if (!asFullAst)
+ return;
+
+ auto scopeType = HighLevelILFunction::GetExprScopeType(trueExpr);
+ tokens.BeginScope(scopeType);
+
+ GetExprTextInternal(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ tokens.EndScope(scopeType);
+ //tokens.SetCurrentExpr(falseExpr);
+ if (falseExpr.operation == HLIL_IF)
+ {
+ tokens.ScopeContinuation(false);
+ tokens.Append(KeywordToken, "else ");
+ GetExprTextInternal(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ }
+ else if (falseExpr.operation != HLIL_NOP)
+ {
+ tokens.ScopeContinuation(false);
+ tokens.Append(KeywordToken, "else");
+ scopeType = HighLevelILFunction::GetExprScopeType(falseExpr);
+ tokens.BeginScope(scopeType);
+ GetExprTextInternal(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+ }
+ else
+ {
+ tokens.FinalizeScope();
+ }
+ }();
+ break;
+
+ case HLIL_WHILE:
+ [&]() {
+ const auto condExpr = instr.GetConditionExpr<HLIL_WHILE>();
+ const auto loopExpr = instr.GetLoopExpr<HLIL_WHILE>();
+
+ tokens.Append(KeywordToken, "while ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(condExpr, tokens, settings);
+ tokens.AppendCloseParen();
+ if (!asFullAst)
+ return;
+
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprTextInternal(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, true);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+
+ }();
+ break;
+
+ case HLIL_DO_WHILE:
+ [&]() {
+ const auto loopExpr = instr.GetLoopExpr<HLIL_DO_WHILE>();
+ const auto condExpr = instr.GetConditionExpr<HLIL_DO_WHILE>();
+ if (asFullAst)
+ {
+ tokens.Append(KeywordToken, "do");
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprTextInternal(loopExpr, tokens, settings, true, 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<HLIL_SWITCH>();
+ const auto caseExprs = instr.GetCases<HLIL_SWITCH>();
+ const auto defaultExpr = instr.GetDefaultExpr<HLIL_SWITCH>();
+
+ tokens.Append(KeywordToken, "switch ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(condExpr, tokens, settings, asFullAst);
+ tokens.AppendCloseParen();
+ tokens.BeginScope(SwitchScopeType);
+ if (!asFullAst)
+ return;
+
+ for (const auto caseExpr : caseExprs)
+ {
+ GetExprTextInternal(caseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ tokens.NewLine();
+ }
+
+ if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE)
+ {
+ tokens.Append(KeywordToken, "default");
+ tokens.Append(TextToken, ":");
+ tokens.BeginScope(CaseScopeType);
+ GetExprTextInternal(defaultExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ tokens.EndScope(CaseScopeType);
+ tokens.FinalizeScope();
+ }
+
+ tokens.EndScope(SwitchScopeType);
+ tokens.FinalizeScope();
+
+ }();
+ break;
+
+ case HLIL_CASE:
+ [&]() {
+ const auto valueExprs = instr.GetValueExprs<HLIL_CASE>();
+ const auto trueExpr = instr.GetTrueExpr<HLIL_CASE>();
+
+ for (size_t index{}; index < valueExprs.size(); index++)
+ {
+ const auto& valueExpr = valueExprs[index];
+ tokens.Append(KeywordToken, "case ");
+ GetExprTextInternal(valueExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, ":");
+ if (index != valueExprs.size() - 1)
+ tokens.NewLine();
+ }
+
+ if (!asFullAst)
+ return;
+
+ tokens.BeginScope(CaseScopeType);
+ GetExprTextInternal(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+
+ static const std::vector<BNHighLevelILOperation> 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<HLIL_BLOCK>();
+ 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.Append(KeywordToken, "break");
+ tokens.Append(TextToken, ";");
+ }
+ }
+ else if (std::find(operations.begin(), operations.end(), trueExpr.operation) == operations.end())
+ {
+ tokens.NewLine();
+ 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<HLIL_ZX>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ AppendSizeToken(instr.size, false, tokens);
+ tokens.AppendCloseParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, false, false);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SX:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_SX>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ AppendSizeToken(instr.size, true, tokens);
+ tokens.AppendCloseParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, false, true);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CALL:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_CALL>();
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_CALL>();
+
+ GetExprTextInternal(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenParen();
+
+ vector<FunctionParameter> namedParams;
+ Ref<Type> functionType = instr.GetDestExpr<HLIL_CALL>().GetType();
+ 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 && (exprType->GetClass() == PointerTypeClass))
+ {
+ if (auto child = exprType->GetChildType(); child)
+ {
+ if ((child->IsInteger() && child->IsSigned() && child->GetWidth() == 1)
+ || child->IsWideChar())
+ {
+ tokens.AppendPointerTextToken(parameterExprs[index],
+ parameterExprs[index].GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols,
+ precedence, true);
+ renderedAsString = true;
+ }
+ }
+ }
+ }
+
+ if (!renderedAsString)
+ GetExprText(parameterExpr, tokens, settings, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_IMPORT:
+ [&]() {
+ const auto constant = instr.GetConstant<HLIL_IMPORT>();
+ 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();
+ }();
+ break;
+
+ case HLIL_ARRAY_INDEX:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ARRAY_INDEX>();
+ const auto indexExpr = instr.GetIndexExpr<HLIL_ARRAY_INDEX>();
+
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenBracket();
+ GetExprTextInternal(indexExpr, tokens, settings, asFullAst);
+ tokens.AppendCloseBracket();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR_INIT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_VAR_INIT>();
+ const auto destExpr = instr.GetDestVariable<HLIL_VAR_INIT>();
+
+ const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(destExpr);
+ const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
+ const auto prevTypeTokens =
+ variableType ?
+ variableType->GetTokensBeforeName(platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+ const auto postTypeTokens =
+ variableType ?
+ variableType->GetTokensAfterName(platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+
+ // 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<HLIL_VAR_INIT_SSA>();
+ 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)
+ {
+ 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)
+ {
+ 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, asFullAst, AssignmentOperatorPrecedence);
+
+ if (appearsDead)
+ tokens.EndForceZeroConfidence();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR_DECLARE:
+ [&]() {
+ const auto variable = instr.GetVariable<HLIL_VAR_DECLARE>();
+
+ const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(variable);
+ const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
+ const auto prevTypeTokens =
+ variableType ?
+ variableType->GetTokensBeforeName(platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+ const auto postTypeTokens =
+ variableType ?
+ variableType->GetTokensAfterName(platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+
+ if (variableType)
+ {
+ 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)
+ {
+ 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<HLIL_FLOAT_CONST>();
+ 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<HLIL_CONST>(), 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<HLIL_CONST_DATA>();
+ if (auto [db, builtin] = data.ToDataBuffer(); db.GetLength())
+ {
+ 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;
+ }
+ default:
+ {
+ if (auto unicode = GetFunction()->GetView()->StringifyUnicodeData(instr.function->GetArchitecture(), db); 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("<invalid constant data>"), instr.address, data.value);
+
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONST_PTR:
+ [&]() {
+ tokens.AppendPointerTextToken(
+ instr, instr.GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, precedence);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR:
+ [&]() {
+ const auto variable = instr.GetVariable<HLIL_VAR>();
+ 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<HLIL_ASSIGN>();
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN>();
+
+ // Check to see if the variable appears live
+ bool appearsDead = false;
+ if (destExpr.operation == HLIL_VAR_SSA)
+ {
+ const auto ssaForm = destExpr.GetSSAVariable<HLIL_VAR_SSA>();
+ 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<HLIL_VAR_SSA>();
+ 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<string> assignUpdateOperator;
+ std::optional<HighLevelILInstruction> assignUpdateSource;
+ bool assignUpdateNegate = false;
+ const auto isSplit = destExpr.operation == HLIL_SPLIT;
+ std::optional<bool> assignSignedHint;
+ if (isSplit)
+ {
+ const auto high = destExpr.GetHighExpr<HLIL_SPLIT>();
+ const auto low = destExpr.GetLowExpr<HLIL_SPLIT>();
+
+ GetExprTextInternal(high, tokens, settings, asFullAst, precedence);
+ tokens.Append(OperationToken, " = ");
+ tokens.Append(OperationToken, "HIGH");
+ AppendSingleSizeToken(high.size, OperationToken, tokens);
+ tokens.AppendOpenParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ tokens.AppendCloseParen();
+ tokens.AppendSemicolon();
+ tokens.NewLine();
+ GetExprTextInternal(low, tokens, settings, asFullAst, precedence);
+ }
+ 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<HLIL_CONST>(), 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;
+ }
+ }
+ }
+
+ GetExprTextInternal(destExpr, tokens, settings, asFullAst, 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 (isSplit)
+ {
+// const auto high = destExpr.GetHighExpr<HLIL_SPLIT>();
+ const auto low = destExpr.GetLowExpr<HLIL_SPLIT>();
+
+ 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<HLIL_CONST>(), assignUpdateSource.value().size, 8),
+ assignUpdateSource.value().size);
+ }
+ else
+ {
+ GetExprTextInternal(assignUpdateSource.value(), tokens, settings, asFullAst,
+ AssignmentOperatorPrecedence, false, assignSignedHint);
+ }
+ }
+ else
+ {
+ GetExprTextInternal(
+ srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence, false, assignSignedHint);
+ }
+
+ if (isSplit)
+ tokens.AppendCloseParen();
+
+ if (appearsDead)
+ tokens.EndForceZeroConfidence();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ASSIGN_UNPACK:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN_UNPACK>();
+ const auto destExprs = instr.GetDestExprs<HLIL_ASSIGN_UNPACK>();
+ const auto firstExpr = destExprs[0];
+
+ GetExprTextInternal(firstExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ tokens.Append(OperationToken, " = ");
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_STRUCT_FIELD:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_STRUCT_FIELD>();
+ const auto fieldOffset = instr.GetOffset<HLIL_STRUCT_FIELD>();
+ const auto memberIndex = instr.GetMemberIndex<HLIL_STRUCT_FIELD>();
+
+ const auto type = GetFieldType(srcExpr, false);
+ const auto fieldDisplayType = GetFieldDisplayType(type, fieldOffset, memberIndex, false);
+ if (fieldDisplayType == FieldDisplayOffset)
+ {
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ AppendSizeToken(!instr.size ? srcExpr.size : instr.size, 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();
+ }
+ GetExprTextInternal(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+
+ tokens.Append(OperationToken, " + ");
+ tokens.AppendIntegerTextToken(instr, fieldOffset, instr.size);
+ tokens.AppendCloseParen();
+
+ char offsetStr[64];
+ snprintf(offsetStr, sizeof(offsetStr), "0x%" PRIx64, fieldOffset);
+
+ vector<string> nameList { offsetStr };
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, srcExpr, nameList);
+ }
+ else if (fieldDisplayType == FieldDisplayMemberOffset)
+ {
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens);
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ tokens.Append(TypeNameToken, "char");
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+ GetExprTextInternal(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendCloseParen();
+ }
+ /* rest is rendered in AppendFieldTextTokens */
+ }
+ else
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ }
+
+ AppendFieldTextTokens(srcExpr, fieldOffset, memberIndex, instr.size, tokens, false);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_DEREF:
+ [&]() {
+ auto srcExpr = instr.GetSourceExpr<HLIL_DEREF>();
+
+ 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<HLIL_CONST_PTR>();
+
+ vector<InstructionTextToken> pointerTokens{};
+ if (AppendPointerTextToken(srcExpr, constant, pointerTokens, settings, DereferenceNonDataSymbols, precedence) == DataSymbolResult)
+ {
+ const auto type = srcExpr.GetType();
+ if (type && 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<HLIL_DEREF>();
+ if (next.size == srcExpr.size)
+ {
+ srcExpr = srcExpr.GetSourceExpr<HLIL_DEREF>();
+ 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, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_TAILCALL:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_TAILCALL>();
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_TAILCALL>();
+
+ tokens.Append(AnnotationToken, "/* tailcall */");
+ tokens.NewLine();
+ tokens.Append(KeywordToken, "return ");
+ GetExprTextInternal(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ 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, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ADDRESS_OF:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ADDRESS_OF>();
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, "&");
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_E:
+ [&]() {
+ bool parens = precedence > EqualityOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendComparison(" == ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CMP_E:
+ [&]() {
+ if ((instr.GetRightExpr<HLIL_CMP_E>().operation == HLIL_CONST
+ || instr.GetRightExpr<HLIL_CMP_E>().operation == HLIL_CONST_PTR)
+ && instr.GetRightExpr<HLIL_CMP_E>().GetConstant() == 0)
+ {
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, "!");
+ GetExprTextInternal(instr.GetLeftExpr<HLIL_CMP_E>(), tokens, settings, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+ else
+ {
+ bool parens = precedence > EqualityOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendComparison(" == ", instr, tokens, settings, asFullAst, 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, asFullAst, EqualityOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CMP_NE:
+ [&]() {
+ if ((instr.GetRightExpr<HLIL_CMP_NE>().operation == HLIL_CONST
+ || instr.GetRightExpr<HLIL_CMP_NE>().operation == HLIL_CONST_PTR)
+ && instr.GetRightExpr<HLIL_CMP_NE>().GetConstant() == 0)
+ {
+ GetExprTextInternal(instr.GetLeftExpr<HLIL_CMP_NE>(), tokens, settings, precedence);
+ }
+ else
+ {
+ bool parens = precedence > EqualityOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendComparison(" != ", instr, tokens, settings, asFullAst, 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<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_ULT)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SLT)
+ cmpSigned = true;
+ AppendComparison(" < ", instr, tokens, settings, asFullAst, 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<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_ULE)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SLE)
+ cmpSigned = true;
+ AppendComparison(" <= ", instr, tokens, settings, asFullAst, 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<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_UGE)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SGE)
+ cmpSigned = true;
+ AppendComparison(" >= ", instr, tokens, settings, asFullAst, 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<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_UGT)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SGT)
+ cmpSigned = true;
+ AppendComparison(" > ", instr, tokens, settings, asFullAst, 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, asFullAst,
+ 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, asFullAst,
+ 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, asFullAst, 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, asFullAst, AddOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SUB:
+ [&]{
+ // Check for offset pointers
+ auto left = instr.GetLeftExpr<HLIL_SUB>();
+ auto right = instr.GetRightExpr<HLIL_SUB>();
+ if (left.operation == HLIL_VAR && right.operation == HLIL_CONST)
+ {
+ auto var = left.GetVariable<HLIL_VAR>();
+ auto srcOffset = right.GetConstant<HLIL_CONST>();
+ auto varType = GetFunction()->GetVariableType(var);
+ if (varType
+ && varType->GetClass() == PointerTypeClass
+ && varType->GetNamedTypeReference()
+ && varType->GetOffset() == srcOffset)
+ {
+ // Yes
+ tokens.Append(OperationToken, "ADJ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(left, tokens, settings, true, 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, asFullAst, 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, asFullAst, 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, asFullAst, 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, asFullAst, 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<bool> mulSigned;
+ if (instr.operation == HLIL_MULU_DP)
+ mulSigned = false;
+ else if (instr.operation == HLIL_MULS_DP)
+ mulSigned = true;
+ AppendTwoOperand(" * ", instr, tokens, settings, asFullAst, 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<bool> 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, asFullAst, 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<bool> 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, asFullAst, 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<HLIL_FLOOR>(), tokens, settings);
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CEIL:
+ [&]() {
+ tokens.Append(OperationToken, "ceil");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(instr.GetSourceExpr<HLIL_CEIL>(), tokens, settings);
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FTRUNC:
+ [&]() {
+ auto src = instr.GetSourceExpr<HLIL_FTRUNC>();
+ 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<HLIL_FABS>();
+ 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<HLIL_FSQRT>();
+ 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:
+ [&]() {
+ AppendTwoOperandFunction("round", instr, tokens, settings, false);
+ 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<HLIL_NOT>();
+ 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, asFullAst, UnaryOperatorPrecedence, false, true);
+ tokens.AppendCloseParen();
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOAT_CONV:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_CONV>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, 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, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOAT_TO_INT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_TO_INT>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ AppendSizeToken(instr.size, true, tokens);
+ tokens.AppendCloseParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_BOOL_TO_INT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_BOOL_TO_INT>();
+
+ bool parens = precedence > TernaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, 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<HLIL_INT_TO_FLOAT>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, 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, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_INTRINSIC:
+ [&]() {
+ const auto intrinsic = instr.GetIntrinsic<HLIL_INTRINSIC>();
+ const auto intrinsicName = GetHighLevelILFunction()->GetArchitecture()->GetIntrinsicName(intrinsic);
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_INTRINSIC>();
+
+ 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, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_RET:
+ [&]() {
+ const auto srcExprs = instr.GetSourceExprs<HLIL_RET>();
+
+ 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, asFullAst);
+ }
+ 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<HLIL_JUMP>();
+ 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<HLIL_TRAP>();
+ 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<HLIL_DEREF_FIELD>();
+ const auto offset = instr.GetOffset<HLIL_DEREF_FIELD>();
+ const auto memberIndex = instr.GetMemberIndex<HLIL_DEREF_FIELD>();
+ 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<size_t> 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<HLIL_CONST_PTR>();
+ symbolType = tokens.AppendPointerTextToken(
+ srcExpr, constant, settings, DisplaySymbolOnly, precedence);
+ }
+ else
+ {
+ GetExprTextInternal(
+ srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ symbolType = OtherSymbolResult;
+ }
+
+ const auto displayDeref = symbolType != DataSymbolResult;
+ if (displayDeref && outer)
+ tokens.Append(OperationToken, "->");
+ else
+ tokens.Append(OperationToken, ".");
+ outer = false;
+
+ vector<string> 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();
+ AppendSizeToken(!derefOffset ? srcExpr.size : instr.size, true, tokens);
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+ tokens.AppendOpenParen();
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ tokens.Append(TypeNameToken, "char");
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+
+ if (srcExpr.operation == HLIL_CONST_PTR)
+ {
+ const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>();
+ tokens.AppendPointerTextToken(srcExpr, constant, settings, DisplaySymbolOnly, precedence);
+ }
+ else
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, true, AddOperatorPrecedence);
+ }
+
+ tokens.Append(OperationToken, " + ");
+ tokens.AppendIntegerTextToken(instr, offset, instr.size);
+ tokens.AppendCloseParen();
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_EXTERN_PTR:
+ [&]() {
+ const int64_t val = instr.GetOffset<HLIL_EXTERN_PTR>();
+ if (val != 0)
+ tokens.AppendOpenParen();
+ tokens.AppendPointerTextToken(
+ instr, instr.GetConstant<HLIL_EXTERN_PTR>(), 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<HLIL_SYSCALL>();
+ vector<FunctionParameter> namedParams;
+ bool skipSyscallNumber = false;
+ if (GetFunction() && (operandList.size() > 0) && (operandList[0].operation == HLIL_CONST))
+ {
+ const auto platform = GetFunction()->GetPlatform();
+ if (platform)
+ {
+ const auto syscall = (uint32_t)operandList[0].GetConstant<HLIL_CONST>();
+ const auto syscallName = platform->GetSystemCallName(syscall);
+ if (settings && settings->GetCallParameterHints() != NeverShowParameterHints)
+ {
+ const auto functionType = platform->GetSystemCallType(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<HLIL_GOTO>();
+ tokens.Append(KeywordToken, "goto ");
+ tokens.Append(GotoLabelToken, GetFunction()->GetGotoLabelName(target), target);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_LABEL:
+ [&]() {
+ const auto target = instr.GetTarget<HLIL_LABEL>();
+ tokens.DecreaseIndent();
+ tokens.Append(GotoLabelToken, GetFunction()->GetGotoLabelName(target), target);
+ tokens.Append(TextToken, ":");
+ tokens.IncreaseIndent();
+ }();
+ break;
+
+ case HLIL_LOW_PART:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_LOW_PART>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, 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, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SPLIT: break;
+ default:
+ [&]() {
+ char buf[64]{};
+ snprintf(buf, sizeof(buf), "/* <UNIMPLEMENTED, %x> */", instr.operation);
+ tokens.Append(AnnotationToken, buf);
+ }();
+ break;
+ }
+
+ if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType())
+ {
+ tokens.AppendCloseParen();
+ }
+}
+
+
+string PseudoCFunction::GetAnnotationStartString() const
+{
+ // Show annotations as C-style inline comments
+ return "/* ";
+}
+
+
+string PseudoCFunction::GetAnnotationEndString() const
+{
+ // Show annotations as C-style inline comments
+ return " */";
+}
+
+
+PseudoCFunctionType::PseudoCFunctionType(): LanguageRepresentationFunctionType("Pseudo C")
+{
+}
+
+
+Ref<LanguageRepresentationFunction> PseudoCFunctionType::Create(Architecture* arch, Function* owner,
+ HighLevelILFunction* highLevelILFunction)
+{
+ return new PseudoCFunction(arch, owner, highLevelILFunction);
+}
+
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+
+#ifndef DEMO_EDITION
+ BINARYNINJAPLUGIN void CorePluginDependencies()
+ {
+ }
+#endif
+
+#ifdef DEMO_EDITION
+ bool PseudoCPluginInit()
+#else
+ BINARYNINJAPLUGIN bool CorePluginInit()
+#endif
+ {
+ LanguageRepresentationFunctionType* type = new PseudoCFunctionType();
+ LanguageRepresentationFunctionType::Register(type);
+ return true;
+ }
+}
diff --git a/lang/c/pseudoc.h b/lang/c/pseudoc.h
new file mode 100644
index 00000000..664e7695
--- /dev/null
+++ b/lang/c/pseudoc.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+
+class PseudoCFunction: public BinaryNinja::LanguageRepresentationFunction
+{
+ BinaryNinja::Ref<BinaryNinja::HighLevelILFunction> m_highLevelIL;
+
+ enum FieldDisplayType
+ {
+ FieldDisplayName,
+ FieldDisplayOffset,
+ FieldDisplayMemberOffset,
+ FieldDisplayNone
+ };
+
+ BinaryNinja::Ref<BinaryNinja::Type> GetFieldType(const BinaryNinja::HighLevelILInstruction& var, bool deref);
+ FieldDisplayType GetFieldDisplayType(BinaryNinja::Ref<BinaryNinja::Type> type, uint64_t offset, size_t memberIndex, bool deref);
+
+ BNSymbolDisplayResult AppendPointerTextToken(const BinaryNinja::HighLevelILInstruction& instr, int64_t val,
+ std::vector<BinaryNinja::InstructionTextToken>& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence);
+ std::string GetSizeToken(size_t size, bool isSigned);
+ void AppendSizeToken(size_t size, bool isSigned, BinaryNinja::HighLevelILTokenEmitter& emitter);
+ void AppendSingleSizeToken(size_t size, BNInstructionTextTokenType type, BinaryNinja::HighLevelILTokenEmitter& emitter);
+ void AppendComparison(const std::string& comparison, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt);
+ void AppendTwoOperand(const std::string& operand, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt);
+ void AppendTwoOperandFunction(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, bool sizeToken = true);
+ void AppendTwoOperandFunctionWithCarry(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings);
+ void AppendFieldTextTokens(const BinaryNinja::HighLevelILInstruction& var, uint64_t offset, size_t memberIndex, size_t size,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, bool deref, bool displayDeref = true);
+ void GetExprTextInternal(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, bool asFullAst = true,
+ BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false,
+ std::optional<bool> signedHint = std::nullopt);
+
+protected:
+ void InitTokenEmitter(BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+ void GetExprText(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, bool asFullAst = true,
+ BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false) override;
+ void BeginLines(
+ const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+ void EndLines(
+ const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+
+public:
+ PseudoCFunction(BinaryNinja::Architecture* arch, BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction);
+
+ std::string GetAnnotationStartString() const override;
+ std::string GetAnnotationEndString() const override;
+};
+
+class PseudoCFunctionType: public BinaryNinja::LanguageRepresentationFunctionType
+{
+public:
+ PseudoCFunctionType();
+ BinaryNinja::Ref<BinaryNinja::LanguageRepresentationFunction> Create(BinaryNinja::Architecture* arch,
+ BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction) override;
+};
diff --git a/lang/rust/CMakeLists.txt b/lang/rust/CMakeLists.txt
new file mode 100644
index 00000000..e374d8e9
--- /dev/null
+++ b/lang/rust/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(lang_pseudorust)
+
+if(NOT BN_INTERNAL_BUILD)
+ add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api)
+endif()
+
+file(GLOB SOURCES
+ *.cpp
+ *.h)
+
+if(DEMO)
+ add_library(lang_pseudorust STATIC ${SOURCES})
+else()
+ add_library(lang_pseudorust SHARED ${SOURCES})
+endif()
+
+target_include_directories(lang_pseudorust
+ PRIVATE ${PROJECT_SOURCE_DIR})
+
+if(WIN32)
+ target_link_directories(lang_pseudorust
+ PRIVATE ${BN_INSTALL_DIR})
+ target_link_libraries(lang_pseudorust binaryninjaapi binaryninjacore)
+else()
+ target_link_libraries(lang_pseudorust binaryninjaapi)
+endif()
+
+set_target_properties(lang_pseudorust PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+ C_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON)
+
+if(BN_INTERNAL_BUILD)
+ plugin_rpath(lang_pseudorust)
+ set_target_properties(lang_pseudorust PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+endif()
diff --git a/lang/rust/pseudorust.cpp b/lang/rust/pseudorust.cpp
new file mode 100644
index 00000000..278bbaa8
--- /dev/null
+++ b/lang/rust/pseudorust.cpp
@@ -0,0 +1,2925 @@
+#include <inttypes.h>
+#include "pseudorust.h"
+#include "rusttypes.h"
+#include "highlevelilinstruction.h"
+
+using namespace std;
+using namespace BinaryNinja;
+
+
+PseudoRustFunction::PseudoRustFunction(
+ Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) :
+ LanguageRepresentationFunction(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);
+}
+
+
+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.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.AppendCloseBrace();
+ }
+}
+
+
+BNSymbolDisplayResult PseudoRustFunction::AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val,
+ vector<InstructionTextToken>& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence)
+{
+ Confidence<Ref<Type>> type = instr.GetType();
+ if (type && (type->GetClass() == PointerTypeClass) && type->IsConst())
+ {
+ string stringValue;
+ size_t childWidth = 0;
+ if (auto child = type->GetChildType(); child)
+ 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 && (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<BinaryView> data = GetFunction()->GetView();
+ vector<InstructionTextToken> 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, bool asFullAst, BNOperatorPrecedence precedence,
+ std::optional<bool> signedHint)
+{
+ const auto leftExpr = instr.GetLeftExpr();
+ const auto rightExpr = instr.GetRightExpr();
+
+ GetExprText(leftExpr, emitter, settings, asFullAst, precedence, InnerExpression, signedHint);
+ emitter.Append(OperationToken, comparison);
+ GetExprText(rightExpr, emitter, settings, asFullAst, precedence, InnerExpression, signedHint);
+}
+
+
+void PseudoRustFunction::AppendTwoOperand(const string& operand, const HighLevelILInstruction& instr,
+ HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence,
+ std::optional<bool> 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, asFullAst);
+ emitter.Append(TextToken, ", ");
+ GetExprText(low, emitter, settings, asFullAst);
+ emitter.AppendCloseParen();
+ }
+
+ if (operand == " + " || operand == " - ")
+ {
+ const auto exprType = leftExpr.GetType();
+ if (exprType && exprType->IsPointer())
+ {
+ GetExprText(leftExpr, emitter, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ emitter.Append(TextToken, ".");
+ emitter.Append(OperationToken, "byte_offset");
+ emitter.AppendOpenParen();
+ if (operand == " - ")
+ {
+ emitter.Append(OperationToken, "-");
+ GetExprText(rightExpr, emitter, settings, asFullAst, UnaryOperatorPrecedence);
+ }
+ else
+ {
+ GetExprText(rightExpr, emitter, settings, asFullAst);
+ }
+ emitter.AppendCloseParen();
+ return;
+ }
+ }
+
+ GetExprText(leftExpr, emitter, settings, asFullAst, 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<HLIL_CONST>(), 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<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size);
+ return;
+ }
+ if ((operand == " - ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), 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<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size);
+ return;
+ }
+
+ emitter.Append(OperationToken, operand);
+ GetExprText(rightExpr, emitter, settings, asFullAst, 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<Type> PseudoRustFunction::GetFieldType(const HighLevelILInstruction& var, bool deref)
+{
+ Ref<Type> 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> type, uint64_t offset, size_t memberIndex, bool deref)
+{
+ if (type && (type->GetClass() == StructureTypeClass))
+ {
+ std::optional<size_t> 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 FieldDisplayMemberOffset;
+ else
+ return FieldDisplayNone;
+}
+
+
+void PseudoRustFunction::AppendFieldTextTokens(const HighLevelILInstruction& var, uint64_t offset,
+ size_t memberIndex, size_t size, HighLevelILTokenEmitter& tokens, bool deref)
+{
+ const auto type = GetFieldType(var, deref);
+ const auto fieldDisplayType = GetFieldDisplayType(type, offset, memberIndex, deref);
+ switch (fieldDisplayType)
+ {
+ case FieldDisplayName:
+ {
+ std::optional<size_t> memberIndexHint;
+ if (memberIndex != BN_INVALID_EXPR)
+ memberIndexHint = memberIndex;
+
+ if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0,
+ [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset,
+ uint64_t adjustedOffset, const StructureMember& member) {
+ tokens.Append(OperationToken, ".");
+
+ vector<string> nameList {member.name};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, var, 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", offset, Type::GetSizeSuffix(size).c_str());
+
+ vector<string> nameList {offsetStr};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(GetFunction()->GetView(), type, var, nameList);
+
+ tokens.Append(StructOffsetToken, offsetStr, offset, size, 0, BN_FULL_CONFIDENCE, nameList);
+ return;
+ }
+
+ case FieldDisplayOffset:
+ {
+ /* this is handled before the display */
+ return;
+ }
+
+ case FieldDisplayMemberOffset:
+ {
+ tokens.AppendOpenBracket();
+ tokens.AppendIntegerTextToken(var, offset, size);
+ tokens.AppendCloseBracket();
+ return;
+ }
+
+ default: break;
+ }
+}
+
+
+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, bool asFullAst, BNOperatorPrecedence precedence, ExpressionType exprType,
+ std::optional<bool> 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())
+ {
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ for (auto& token: instr.GetType()->GetTokens(GetArchitecture()->GetStandalonePlatform()))
+ {
+ tokens.Append(token);
+ }
+ tokens.AppendCloseParen();
+ tokens.Append(TextToken, " ");
+ }
+ if (settings && settings->IsOptionSet(ShowILOpcodes))
+ {
+ tokens.Append(OperationToken, "/*");
+ switch (instr.operation)
+ {
+ case HLIL_NOP: tokens.Append(OperationToken, "HLIL_NOP"); break;
+ case HLIL_BLOCK: tokens.Append(OperationToken, "HLIL_BLOCK"); break;
+ case HLIL_IF: tokens.Append(OperationToken, "HLIL_IF"); break;
+ case HLIL_WHILE: tokens.Append(OperationToken, "HLIL_WHILE"); break;
+ case HLIL_DO_WHILE: tokens.Append(OperationToken, "HLIL_DO_WHILE"); break;
+ case HLIL_FOR: tokens.Append(OperationToken, "HLIL_FOR"); break;
+ case HLIL_SWITCH: tokens.Append(OperationToken, "HLIL_SWITCH"); break;
+ case HLIL_CASE: tokens.Append(OperationToken, "HLIL_CASE"); break;
+ case HLIL_BREAK: tokens.Append(OperationToken, "HLIL_BREAK"); break;
+ case HLIL_CONTINUE: tokens.Append(OperationToken, "HLIL_CONTINUE"); break;
+ case HLIL_JUMP: tokens.Append(OperationToken, "HLIL_JUMP"); break;
+ case HLIL_RET: tokens.Append(OperationToken, "HLIL_RET"); break;
+ case HLIL_NORET: tokens.Append(OperationToken, "HLIL_NORET"); break;
+ case HLIL_GOTO: tokens.Append(OperationToken, "HLIL_GOTO"); break;
+ case HLIL_LABEL: tokens.Append(OperationToken, "HLIL_LABEL"); break;
+ case HLIL_VAR_DECLARE: tokens.Append(OperationToken, "HLIL_VAR_DECLARE"); break;
+ case HLIL_VAR_INIT: tokens.Append(OperationToken, "HLIL_VAR_INIT"); break;
+ case HLIL_ASSIGN: tokens.Append(OperationToken, "HLIL_ASSIGN"); break;
+ case HLIL_ASSIGN_UNPACK: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK"); break;
+ case HLIL_VAR: tokens.Append(OperationToken, "HLIL_VAR"); break;
+ case HLIL_STRUCT_FIELD: tokens.Append(OperationToken, "HLIL_STRUCT_FIELD"); break;
+ case HLIL_ARRAY_INDEX: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX"); break;
+ case HLIL_SPLIT: tokens.Append(OperationToken, "HLIL_SPLIT"); break;
+ case HLIL_DEREF: tokens.Append(OperationToken, "HLIL_DEREF"); break;
+ case HLIL_DEREF_FIELD: tokens.Append(OperationToken, "HLIL_DEREF_FIELD"); break;
+ case HLIL_ADDRESS_OF: tokens.Append(OperationToken, "HLIL_ADDRESS_OF"); break;
+ case HLIL_CONST: tokens.Append(OperationToken, "HLIL_CONST"); break;
+ case HLIL_CONST_DATA: tokens.Append(OperationToken, "HLIL_CONST_DATA"); break;
+ case HLIL_CONST_PTR: tokens.Append(OperationToken, "HLIL_CONST_PTR"); break;
+ case HLIL_EXTERN_PTR: tokens.Append(OperationToken, "HLIL_EXTERN_PTR"); break;
+ case HLIL_FLOAT_CONST: tokens.Append(OperationToken, "HLIL_FLOAT_CONST"); break;
+ case HLIL_IMPORT: tokens.Append(OperationToken, "HLIL_IMPORT"); break;
+ case HLIL_ADD: tokens.Append(OperationToken, "HLIL_ADD"); break;
+ case HLIL_ADC: tokens.Append(OperationToken, "HLIL_ADC"); break;
+ case HLIL_SUB: tokens.Append(OperationToken, "HLIL_SUB"); break;
+ case HLIL_SBB: tokens.Append(OperationToken, "HLIL_SBB"); break;
+ case HLIL_AND: tokens.Append(OperationToken, "HLIL_AND"); break;
+ case HLIL_OR: tokens.Append(OperationToken, "HLIL_OR"); break;
+ case HLIL_XOR: tokens.Append(OperationToken, "HLIL_XOR"); break;
+ case HLIL_LSL: tokens.Append(OperationToken, "HLIL_LSL"); break;
+ case HLIL_LSR: tokens.Append(OperationToken, "HLIL_LSR"); break;
+ case HLIL_ASR: tokens.Append(OperationToken, "HLIL_ASR"); break;
+ case HLIL_ROL: tokens.Append(OperationToken, "HLIL_ROL"); break;
+ case HLIL_RLC: tokens.Append(OperationToken, "HLIL_RLC"); break;
+ case HLIL_ROR: tokens.Append(OperationToken, "HLIL_ROR"); break;
+ case HLIL_RRC: tokens.Append(OperationToken, "HLIL_RRC"); break;
+ case HLIL_MUL: tokens.Append(OperationToken, "HLIL_MUL"); break;
+ case HLIL_MULU_DP: tokens.Append(OperationToken, "HLIL_MULU_DP"); break;
+ case HLIL_MULS_DP: tokens.Append(OperationToken, "HLIL_MULS_DP"); break;
+ case HLIL_DIVU: tokens.Append(OperationToken, "HLIL_DIVU"); break;
+ case HLIL_DIVU_DP: tokens.Append(OperationToken, "HLIL_DIVU_DP"); break;
+ case HLIL_DIVS: tokens.Append(OperationToken, "HLIL_DIVS"); break;
+ case HLIL_DIVS_DP: tokens.Append(OperationToken, "HLIL_DIVS_DP"); break;
+ case HLIL_MODU: tokens.Append(OperationToken, "HLIL_MODU"); break;
+ case HLIL_MODU_DP: tokens.Append(OperationToken, "HLIL_MODU_DP"); break;
+ case HLIL_MODS: tokens.Append(OperationToken, "HLIL_MODS"); break;
+ case HLIL_MODS_DP: tokens.Append(OperationToken, "HLIL_MODS_DP"); break;
+ case HLIL_NEG: tokens.Append(OperationToken, "HLIL_NEG"); break;
+ case HLIL_NOT: tokens.Append(OperationToken, "HLIL_NOT"); break;
+ case HLIL_SX: tokens.Append(OperationToken, "HLIL_SX"); break;
+ case HLIL_ZX: tokens.Append(OperationToken, "HLIL_ZX"); break;
+ case HLIL_LOW_PART: tokens.Append(OperationToken, "HLIL_LOW_PART"); break;
+ case HLIL_CALL: tokens.Append(OperationToken, "HLIL_CALL"); break;
+ case HLIL_CMP_E: tokens.Append(OperationToken, "HLIL_CMP_E"); break;
+ case HLIL_CMP_NE: tokens.Append(OperationToken, "HLIL_CMP_NE"); break;
+ case HLIL_CMP_SLT: tokens.Append(OperationToken, "HLIL_CMP_SLT"); break;
+ case HLIL_CMP_ULT: tokens.Append(OperationToken, "HLIL_CMP_ULT"); break;
+ case HLIL_CMP_SLE: tokens.Append(OperationToken, "HLIL_CMP_SLE"); break;
+ case HLIL_CMP_ULE: tokens.Append(OperationToken, "HLIL_CMP_ULE"); break;
+ case HLIL_CMP_SGE: tokens.Append(OperationToken, "HLIL_CMP_SGE"); break;
+ case HLIL_CMP_UGE: tokens.Append(OperationToken, "HLIL_CMP_UGE"); break;
+ case HLIL_CMP_SGT: tokens.Append(OperationToken, "HLIL_CMP_SGT"); break;
+ case HLIL_CMP_UGT: tokens.Append(OperationToken, "HLIL_CMP_UGT"); break;
+ case HLIL_TEST_BIT: tokens.Append(OperationToken, "HLIL_TEST_BIT"); break;
+ case HLIL_BOOL_TO_INT: tokens.Append(OperationToken, "HLIL_BOOL_TO_INT"); break;
+ case HLIL_ADD_OVERFLOW: tokens.Append(OperationToken, "HLIL_ADD_OVERFLOW"); break;
+ case HLIL_SYSCALL: tokens.Append(OperationToken, "HLIL_SYSCALL"); break;
+ case HLIL_TAILCALL: tokens.Append(OperationToken, "HLIL_TAILCALL"); break;
+ case HLIL_INTRINSIC: tokens.Append(OperationToken, "HLIL_INTRINSIC"); break;
+ case HLIL_BP: tokens.Append(OperationToken, "HLIL_BP"); break;
+ case HLIL_TRAP: tokens.Append(OperationToken, "HLIL_TRAP"); break;
+ case HLIL_UNDEF: tokens.Append(OperationToken, "HLIL_UNDEF"); break;
+ case HLIL_UNIMPL: tokens.Append(OperationToken, "HLIL_UNIMPL"); break;
+ case HLIL_UNIMPL_MEM: tokens.Append(OperationToken, "HLIL_UNIMPL_MEM"); break;
+ case HLIL_FADD: tokens.Append(OperationToken, "HLIL_FADD"); break;
+ case HLIL_FSUB: tokens.Append(OperationToken, "HLIL_FSUB"); break;
+ case HLIL_FMUL: tokens.Append(OperationToken, "HLIL_FMUL"); break;
+ case HLIL_FDIV: tokens.Append(OperationToken, "HLIL_FDIV"); break;
+ case HLIL_FSQRT: tokens.Append(OperationToken, "HLIL_FSQRT"); break;
+ case HLIL_FNEG: tokens.Append(OperationToken, "HLIL_FNEG"); break;
+ case HLIL_FABS: tokens.Append(OperationToken, "HLIL_FABS"); break;
+ case HLIL_FLOAT_TO_INT: tokens.Append(OperationToken, "HLIL_FLOAT_TO_INT"); break;
+ case HLIL_INT_TO_FLOAT: tokens.Append(OperationToken, "HLIL_INT_TO_FLOAT"); break;
+ case HLIL_FLOAT_CONV: tokens.Append(OperationToken, "HLIL_FLOAT_CONV"); break;
+ case HLIL_ROUND_TO_INT: tokens.Append(OperationToken, "HLIL_ROUND_TO_INT"); break;
+ case HLIL_FLOOR: tokens.Append(OperationToken, "HLIL_FLOOR"); break;
+ case HLIL_CEIL: tokens.Append(OperationToken, "HLIL_CEIL"); break;
+ case HLIL_FTRUNC: tokens.Append(OperationToken, "HLIL_FTRUNC"); break;
+ case HLIL_FCMP_E: tokens.Append(OperationToken, "HLIL_FCMP_E"); break;
+ case HLIL_FCMP_NE: tokens.Append(OperationToken, "HLIL_FCMP_NE"); break;
+ case HLIL_FCMP_LT: tokens.Append(OperationToken, "HLIL_FCMP_LT"); break;
+ case HLIL_FCMP_LE: tokens.Append(OperationToken, "HLIL_FCMP_LE"); break;
+ case HLIL_FCMP_GE: tokens.Append(OperationToken, "HLIL_FCMP_GE"); break;
+ case HLIL_FCMP_GT: tokens.Append(OperationToken, "HLIL_FCMP_GT"); break;
+ case HLIL_FCMP_O: tokens.Append(OperationToken, "HLIL_FCMP_O"); break;
+ case HLIL_FCMP_UO: tokens.Append(OperationToken, "HLIL_FCMP_UO"); break;
+ case HLIL_UNREACHABLE: tokens.Append(OperationToken, "HLIL_UNREACHABLE"); break;
+ case HLIL_WHILE_SSA: tokens.Append(OperationToken, "HLIL_WHILE_SSA"); break;
+ case HLIL_DO_WHILE_SSA: tokens.Append(OperationToken, "HLIL_DO_WHILE_SSA"); break;
+ case HLIL_FOR_SSA: tokens.Append(OperationToken, "HLIL_FOR_SSA"); break;
+ case HLIL_VAR_INIT_SSA: tokens.Append(OperationToken, "HLIL_VAR_INIT_SSA"); break;
+ case HLIL_ASSIGN_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_MEM_SSA"); break;
+ case HLIL_ASSIGN_UNPACK_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK_MEM_SSA"); break;
+ case HLIL_VAR_SSA: tokens.Append(OperationToken, "HLIL_VAR_SSA"); break;
+ case HLIL_ARRAY_INDEX_SSA: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX_SSA"); break;
+ case HLIL_DEREF_SSA: tokens.Append(OperationToken, "HLIL_DEREF_SSA"); break;
+ case HLIL_DEREF_FIELD_SSA: tokens.Append(OperationToken, "HLIL_DEREF_FIELD_SSA"); break;
+ case HLIL_CALL_SSA: tokens.Append(OperationToken, "HLIL_CALL_SSA"); break;
+ case HLIL_SYSCALL_SSA: tokens.Append(OperationToken, "HLIL_SYSCALL_SSA"); break;
+ case HLIL_INTRINSIC_SSA: tokens.Append(OperationToken, "HLIL_INTRINSIC_SSA"); break;
+ case HLIL_VAR_PHI: tokens.Append(OperationToken, "HLIL_VAR_PHI"); break;
+ case HLIL_MEM_PHI: tokens.Append(OperationToken, "HLIL_MEM_PHI"); break;
+ }
+ tokens.Append(OperationToken, "*/");
+ tokens.Append(TextToken, " ");
+ }
+
+ switch (instr.operation)
+ {
+ case HLIL_BLOCK:
+ [&]() {
+ const auto exprs = instr.GetBlockExprs<HLIL_BLOCK>();
+ 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 (asFullAst && (instr.exprIndex == GetHighLevelILFunction()->GetRootExpr().exprIndex) && (exprs.size() > 1) &&
+ (next == exprs.end()) && ((*i).operation == HLIL_RET) &&
+ ((*i).GetSourceExprs<HLIL_RET>().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, true, TopLevelOperatorPrecedence,
+ exprType == TrailingStatementExpression && next == exprs.end() ?
+ TrailingStatementExpression : StatementExpression);
+ tokens.NewLine();
+ }
+ }();
+ break;
+
+ case HLIL_FOR:
+ [&]() {
+ const auto initExpr = instr.GetInitExpr<HLIL_FOR>();
+ const auto condExpr = instr.GetConditionExpr<HLIL_FOR>();
+ const auto updateExpr = instr.GetUpdateExpr<HLIL_FOR>();
+ const auto loopExpr = instr.GetLoopExpr<HLIL_FOR>();
+
+ if (asFullAst)
+ {
+ 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<HLIL_VAR>() == initExpr.GetDestVariable<HLIL_VAR_INIT>() &&
+ updateExpr.operation == HLIL_ASSIGN &&
+ updateExpr.GetDestExpr<HLIL_ASSIGN>() == condExpr.GetLeftExpr() &&
+ updateExpr.GetSourceExpr<HLIL_ASSIGN>().operation == HLIL_ADD &&
+ updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetLeftExpr<HLIL_ADD>() == condExpr.GetLeftExpr())
+ {
+ bool stepBy = updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetRightExpr<HLIL_ADD>().operation != HLIL_CONST ||
+ updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetRightExpr<HLIL_ADD>().GetConstant() != 1;
+
+ const auto variable = initExpr.GetDestVariable<HLIL_VAR_INIT>();
+ 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<HLIL_VAR_INIT>(), tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ if (condExpr.operation == HLIL_CMP_SLT || condExpr.operation == HLIL_CMP_ULT)
+ tokens.Append(TextToken, "..");
+ else
+ tokens.Append(TextToken, "..=");
+ GetExprText(condExpr.GetRightExpr(), tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+
+ if (stepBy)
+ {
+ tokens.AppendCloseParen();
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "step_by");
+ tokens.AppendOpenParen();
+ GetExprText(updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetRightExpr<HLIL_ADD>(),
+ tokens, settings, asFullAst);
+ tokens.AppendCloseParen();
+ }
+ }
+ else
+ {
+ // For loop isn't directly representable in standard Rust
+ if (initExpr.operation != HLIL_NOP)
+ GetExprText(initExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, "; ");
+ if (condExpr.operation != HLIL_NOP)
+ GetExprText(condExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, "; ");
+ if (updateExpr.operation != HLIL_NOP)
+ GetExprText(updateExpr, tokens, settings, asFullAst);
+ }
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprText(loopExpr, tokens, settings, asFullAst, 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<HLIL_IF>();
+ const auto trueExpr = instr.GetTrueExpr<HLIL_IF>();
+ const auto falseExpr = instr.GetFalseExpr<HLIL_IF>();
+
+ tokens.Append(KeywordToken, "if ");
+ GetExprText(condExpr, tokens, settings, asFullAst);
+ if (!asFullAst)
+ return;
+
+ auto scopeType = HighLevelILFunction::GetExprScopeType(trueExpr);
+ tokens.BeginScope(scopeType);
+
+ GetExprText(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ tokens.EndScope(scopeType);
+ //tokens.SetCurrentExpr(falseExpr);
+ if (falseExpr.operation == HLIL_IF)
+ {
+ tokens.ScopeContinuation(false);
+ tokens.Append(KeywordToken, "else ");
+ GetExprText(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ }
+ else if (falseExpr.operation != HLIL_NOP)
+ {
+ tokens.ScopeContinuation(false);
+ tokens.Append(KeywordToken, "else");
+ scopeType = HighLevelILFunction::GetExprScopeType(falseExpr);
+ tokens.BeginScope(scopeType);
+ GetExprText(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+ }
+ else
+ {
+ tokens.FinalizeScope();
+ }
+ }();
+ break;
+
+ case HLIL_WHILE:
+ [&]() {
+ const auto condExpr = instr.GetConditionExpr<HLIL_WHILE>();
+ const auto loopExpr = instr.GetLoopExpr<HLIL_WHILE>();
+
+ if (condExpr.operation == HLIL_CONST && condExpr.GetConstant<HLIL_CONST>() != 0)
+ {
+ tokens.Append(KeywordToken, "loop");
+ }
+ else
+ {
+ tokens.Append(KeywordToken, "while ");
+ GetExprText(condExpr, tokens, settings);
+ }
+ if (!asFullAst)
+ return;
+
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprText(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, StatementExpression);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+
+ }();
+ break;
+
+ case HLIL_DO_WHILE:
+ [&]() {
+ const auto loopExpr = instr.GetLoopExpr<HLIL_DO_WHILE>();
+ const auto condExpr = instr.GetConditionExpr<HLIL_DO_WHILE>();
+ if (asFullAst)
+ {
+ tokens.Append(KeywordToken, "do");
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprText(loopExpr, tokens, settings, true, 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<HLIL_SWITCH>();
+ const auto caseExprs = instr.GetCases<HLIL_SWITCH>();
+ const auto defaultExpr = instr.GetDefaultExpr<HLIL_SWITCH>();
+
+ tokens.Append(KeywordToken, "match ");
+ GetExprText(condExpr, tokens, settings, asFullAst);
+ tokens.BeginScope(SwitchScopeType);
+ if (!asFullAst)
+ return;
+
+ for (const auto caseExpr : caseExprs)
+ {
+ GetExprText(caseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ tokens.NewLine();
+ }
+
+ // Check for default case
+ if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE)
+ {
+ tokens.Append(TextToken, "_ =>");
+ tokens.BeginScope(CaseScopeType);
+ GetExprText(defaultExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ tokens.EndScope(CaseScopeType);
+ tokens.FinalizeScope();
+ }
+
+ tokens.EndScope(SwitchScopeType);
+ tokens.FinalizeScope();
+
+ }();
+ break;
+
+ case HLIL_CASE:
+ [&]() {
+ const auto valueExprs = instr.GetValueExprs<HLIL_CASE>();
+ const auto trueExpr = instr.GetTrueExpr<HLIL_CASE>();
+
+ for (size_t index{}; index < valueExprs.size(); index++)
+ {
+ const auto& valueExpr = valueExprs[index];
+ if (index != 0)
+ tokens.Append(TextToken, " | ");
+ GetExprText(valueExpr, tokens, settings, asFullAst);
+ }
+ tokens.Append(TextToken, " =>");
+
+ if (!asFullAst)
+ return;
+
+ tokens.BeginScope(CaseScopeType);
+ GetExprText(trueExpr, tokens, settings, asFullAst, 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<HLIL_ZX>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, 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<HLIL_SX>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, 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<HLIL_CALL>();
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_CALL>();
+
+ GetExprText(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenParen();
+
+ vector<FunctionParameter> namedParams;
+ Ref<Type> functionType = instr.GetDestExpr<HLIL_CALL>().GetType();
+ 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 && (exprType->GetClass() == PointerTypeClass))
+ {
+ if (auto child = exprType->GetChildType(); child)
+ {
+ if ((child->IsInteger() && child->IsSigned() && child->GetWidth() == 1)
+ || child->IsWideChar())
+ {
+ tokens.AppendPointerTextToken(parameterExprs[index],
+ parameterExprs[index].GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols,
+ precedence, true);
+ renderedAsString = true;
+ }
+ }
+ }
+ }
+
+ if (!renderedAsString)
+ GetExprText(parameterExpr, tokens, settings, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_IMPORT:
+ [&]() {
+ const auto constant = instr.GetConstant<HLIL_IMPORT>();
+ 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<HLIL_ARRAY_INDEX>();
+ const auto indexExpr = instr.GetIndexExpr<HLIL_ARRAY_INDEX>();
+
+ GetExprText(srcExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenBracket();
+ GetExprText(indexExpr, tokens, settings, asFullAst);
+ tokens.AppendCloseBracket();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR_INIT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_VAR_INIT>();
+ const auto destExpr = instr.GetDestVariable<HLIL_VAR_INIT>();
+
+ const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(destExpr);
+ const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
+ RustTypePrinter printer;
+ const auto prevTypeTokens = variableType ?
+ printer.GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken> {};
+ const auto postTypeTokens = variableType ?
+ printer.GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken> {};
+
+ // 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<HLIL_VAR_INIT_SSA>();
+ 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)
+ {
+ for (auto typeToken : prevTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = destExpr.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ }
+ tokens.AppendVarTextToken(destExpr, instr, instr.size);
+ if (variableType)
+ {
+ 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, asFullAst, AssignmentOperatorPrecedence);
+
+ if (appearsDead)
+ tokens.EndForceZeroConfidence();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR_DECLARE:
+ [&]() {
+ const auto variable = instr.GetVariable<HLIL_VAR_DECLARE>();
+
+ const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(variable);
+ const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
+ RustTypePrinter printer;
+ const auto prevTypeTokens =
+ variableType ?
+ printer.GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+ const auto postTypeTokens =
+ variableType ?
+ printer.GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+
+ tokens.Append(KeywordToken, "let ");
+
+ // Only show `mut` keyword if the variable is actually changed
+ if (IsMutable(variable))
+ tokens.Append(KeywordToken, "mut ");
+
+ if (variableType)
+ {
+ for (auto typeToken: prevTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = variable.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ }
+ tokens.AppendVarTextToken(variable, instr, instr.size);
+ if (variableType)
+ {
+ 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<HLIL_FLOAT_CONST>();
+ 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<HLIL_CONST>(), 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<HLIL_CONST_DATA>();
+ if (auto [db, builtin] = data.ToDataBuffer(); db.GetLength())
+ {
+ 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;
+ }
+ default:
+ {
+ if (auto unicode = GetFunction()->GetView()->StringifyUnicodeData(instr.function->GetArchitecture(), db); 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("<invalid constant data>"), instr.address, data.value);
+
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONST_PTR:
+ [&]() {
+ tokens.AppendPointerTextToken(
+ instr, instr.GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, precedence);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR:
+ [&]() {
+ const auto variable = instr.GetVariable<HLIL_VAR>();
+ 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<HLIL_ASSIGN>();
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN>();
+
+ // Check to see if the variable appears live
+ bool appearsDead = false;
+ if (destExpr.operation == HLIL_VAR_SSA)
+ {
+ const auto ssaForm = destExpr.GetSSAVariable<HLIL_VAR_SSA>();
+ 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<HLIL_VAR_SSA>();
+ 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<string> assignUpdateOperator;
+ std::optional<HighLevelILInstruction> assignUpdateSource;
+ bool assignUpdateNegate = false;
+ const auto isSplit = destExpr.operation == HLIL_SPLIT;
+ std::optional<bool> assignSignHint;
+ if (isSplit)
+ {
+ const auto high = destExpr.GetHighExpr<HLIL_SPLIT>();
+ const auto low = destExpr.GetLowExpr<HLIL_SPLIT>();
+
+ GetExprText(high, tokens, settings, asFullAst, precedence);
+ tokens.Append(OperationToken, " = ");
+ tokens.Append(OperationToken, "HIGH");
+ AppendSingleSizeToken(high.size, OperationToken, tokens);
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ tokens.AppendCloseParen();
+ tokens.AppendSemicolon();
+ tokens.NewLine();
+ GetExprText(low, tokens, settings, asFullAst, precedence);
+ }
+ 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<HLIL_CONST>(), 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, asFullAst, 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 (isSplit)
+ {
+// const auto high = destExpr.GetHighExpr<HLIL_SPLIT>();
+ const auto low = destExpr.GetLowExpr<HLIL_SPLIT>();
+
+ 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<HLIL_CONST>(), assignUpdateSource.value().size, 8),
+ assignUpdateSource.value().size);
+ }
+ else
+ {
+ GetExprText(assignUpdateSource.value(), tokens, settings, asFullAst, AssignmentOperatorPrecedence,
+ InnerExpression, assignSignHint);
+ }
+ }
+ else
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence, InnerExpression,
+ assignSignHint);
+ }
+
+ if (isSplit)
+ tokens.AppendCloseParen();
+
+ if (appearsDead)
+ tokens.EndForceZeroConfidence();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ASSIGN_UNPACK:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN_UNPACK>();
+ const auto destExprs = instr.GetDestExprs<HLIL_ASSIGN_UNPACK>();
+ const auto firstExpr = destExprs[0];
+
+ GetExprText(firstExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ tokens.Append(OperationToken, " = ");
+ GetExprText(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_STRUCT_FIELD:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_STRUCT_FIELD>();
+ const auto fieldOffset = instr.GetOffset<HLIL_STRUCT_FIELD>();
+ const auto memberIndex = instr.GetMemberIndex<HLIL_STRUCT_FIELD>();
+
+ const auto type = GetFieldType(srcExpr, false);
+ const auto fieldDisplayType = GetFieldDisplayType(type, fieldOffset, memberIndex, false);
+ if (fieldDisplayType == FieldDisplayOffset)
+ {
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ tokens.AppendOpenParen();
+
+ GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+
+ 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<Type> srcType = srcExpr.GetType();
+ if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst())
+ tokens.Append(KeywordToken, "const ");
+ else
+ tokens.Append(KeywordToken, "mut ");
+ AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens);
+ tokens.AppendCloseParen();
+ }
+
+ char offsetStr[64];
+ snprintf(offsetStr, sizeof(offsetStr), "0x%" PRIx64, fieldOffset);
+
+ vector<string> nameList { offsetStr };
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, srcExpr, nameList);
+ }
+ else if (fieldDisplayType == FieldDisplayMemberOffset)
+ {
+ tokens.Append(OperationToken, "*");
+ BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence;
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ srcPrecedence = LowUnaryOperatorPrecedence;
+ }
+ GetExprText(srcExpr, tokens, settings, true, srcPrecedence);
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.Append(KeywordToken, " as ");
+ tokens.Append(TextToken, "*");
+ Ref<Type> srcType = srcExpr.GetType();
+ if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst())
+ tokens.Append(KeywordToken, "const ");
+ else
+ tokens.Append(KeywordToken, "mut ");
+ AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens);
+ tokens.AppendCloseParen();
+ }
+ /* rest is rendered in AppendFieldTextTokens */
+ }
+ else
+ {
+ GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ }
+
+ AppendFieldTextTokens(srcExpr, fieldOffset, memberIndex, instr.size, tokens, false);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_DEREF:
+ [&]() {
+ auto srcExpr = instr.GetSourceExpr<HLIL_DEREF>();
+
+ 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<HLIL_CONST_PTR>();
+
+ const auto type = srcExpr.GetType();
+ BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence;
+ if (type && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth() &&
+ (!settings || settings->IsOptionSet(ShowTypeCasts)))
+ srcPrecedence = LowUnaryOperatorPrecedence;
+
+ vector<InstructionTextToken> pointerTokens{};
+ if (AppendPointerTextToken(srcExpr, constant, pointerTokens, settings, DereferenceNonDataSymbols, srcPrecedence) == DataSymbolResult)
+ {
+ if (type && 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<Type> srcType = srcExpr.GetType();
+ 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<bool> derefConst;
+ Ref<Type> srcType = srcExpr.GetType().GetValue();
+ derefConst.push_back(srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst());
+ while (srcExpr.operation == HLIL_DEREF)
+ {
+ auto next = srcExpr.GetSourceExpr<HLIL_DEREF>();
+ if (next.size == srcExpr.size)
+ {
+ srcType = srcExpr.GetType().GetValue();
+ derefConst.push_back(srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst());
+ srcExpr = srcExpr.GetSourceExpr<HLIL_DEREF>();
+ }
+ 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, asFullAst, 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<HLIL_TAILCALL>();
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_TAILCALL>();
+
+ tokens.Append(AnnotationToken, "/* tailcall */");
+ tokens.NewLine();
+ if (exprType != TrailingStatementExpression)
+ tokens.Append(KeywordToken, "return ");
+ GetExprText(destExpr, tokens, settings, asFullAst, 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, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (exprType == StatementExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ADDRESS_OF:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ADDRESS_OF>();
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, "&");
+ GetExprText(srcExpr, tokens, settings, asFullAst, 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, asFullAst, 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, asFullAst, 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<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_ULT)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SLT)
+ cmpSigned = true;
+ AppendComparison(" < ", instr, tokens, settings, asFullAst, 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<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_ULE)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SLE)
+ cmpSigned = true;
+ AppendComparison(" <= ", instr, tokens, settings, asFullAst, 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<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_UGE)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SGE)
+ cmpSigned = true;
+ AppendComparison(" >= ", instr, tokens, settings, asFullAst, 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<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_UGT)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SGT)
+ cmpSigned = true;
+ AppendComparison(" > ", instr, tokens, settings, asFullAst, 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, asFullAst,
+ 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, asFullAst,
+ 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, asFullAst, 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 && 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, asFullAst, opPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SUB:
+ [&]{
+ // Check for offset pointers
+ auto left = instr.GetLeftExpr<HLIL_SUB>();
+ auto right = instr.GetRightExpr<HLIL_SUB>();
+ if (left.operation == HLIL_VAR && right.operation == HLIL_CONST)
+ {
+ auto var = left.GetVariable<HLIL_VAR>();
+ auto srcOffset = right.GetConstant<HLIL_CONST>();
+ auto varType = GetFunction()->GetVariableType(var);
+ if (varType
+ && varType->GetClass() == PointerTypeClass
+ && varType->GetNamedTypeReference()
+ && varType->GetOffset() == srcOffset)
+ {
+ // Yes
+ tokens.Append(OperationToken, "ADJ");
+ tokens.AppendOpenParen();
+ GetExprText(left, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendCloseParen();
+ return;
+ }
+ }
+
+ const auto leftType = instr.GetLeftExpr().GetType();
+ bool parens;
+ BNOperatorPrecedence opPrecedence = SubOperatorPrecedence;
+ if (leftType && 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, asFullAst, 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, asFullAst, 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, asFullAst, 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, asFullAst, 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<bool> mulSigned;
+ if (instr.operation == HLIL_MULU_DP)
+ mulSigned = false;
+ else if (instr.operation == HLIL_MULS_DP)
+ mulSigned = true;
+ AppendTwoOperand(" * ", instr, tokens, settings, asFullAst, 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<bool> 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, asFullAst, 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<bool> 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, asFullAst, 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<HLIL_FLOOR>(), 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<HLIL_CEIL>(), 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<HLIL_FTRUNC>(), 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<HLIL_FABS>(), 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<HLIL_FSQRT>(), 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<HLIL_ROUND_TO_INT>(), 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<HLIL_NOT>();
+ 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, asFullAst, UnaryOperatorPrecedence, InnerExpression, true);
+ tokens.AppendCloseParen();
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOAT_CONV:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_CONV>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ const auto floatType = "f" + std::to_string(instr.size * 8);
+
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, 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<HLIL_FLOAT_TO_INT>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, 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<HLIL_BOOL_TO_INT>();
+
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, 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<HLIL_INT_TO_FLOAT>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ const auto floatType = "f" + std::to_string(instr.size * 8);
+
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, 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<HLIL_INTRINSIC>();
+ const auto intrinsicName = GetHighLevelILFunction()->GetArchitecture()->GetIntrinsicName(intrinsic);
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_INTRINSIC>();
+
+ 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, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_RET:
+ [&]() {
+ const auto srcExprs = instr.GetSourceExprs<HLIL_RET>();
+
+ if (!asFullAst || exprType != TrailingStatementExpression)
+ tokens.Append(KeywordToken, "return");
+ if (srcExprs.size() != 0)
+ {
+ if (!asFullAst || 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, asFullAst);
+ }
+ 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<HLIL_JUMP>();
+ 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<HLIL_TRAP>();
+ 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<HLIL_DEREF_FIELD>();
+ const auto offset = instr.GetOffset<HLIL_DEREF_FIELD>();
+ const auto memberIndex = instr.GetMemberIndex<HLIL_DEREF_FIELD>();
+ 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<size_t> 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<HLIL_CONST_PTR>();
+ symbolType = tokens.AppendPointerTextToken(
+ srcExpr, constant, settings, DisplaySymbolOnly, precedence);
+ }
+ else
+ {
+ GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ symbolType = OtherSymbolResult;
+ }
+
+ const auto displayDeref = symbolType != DataSymbolResult;
+ if (displayDeref && outer)
+ tokens.Append(OperationToken, "->");
+ else
+ tokens.Append(OperationToken, ".");
+ outer = false;
+
+ vector<string> 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<HLIL_CONST_PTR>();
+ tokens.AppendPointerTextToken(srcExpr, constant, settings, DisplaySymbolOnly, precedence);
+ }
+ else
+ {
+ GetExprText(srcExpr, tokens, settings, true, 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<Type> srcType = srcExpr.GetType();
+ 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<HLIL_EXTERN_PTR>();
+ if (val != 0)
+ tokens.AppendOpenParen();
+ tokens.AppendPointerTextToken(
+ instr, instr.GetConstant<HLIL_EXTERN_PTR>(), 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<HLIL_SYSCALL>();
+ vector<FunctionParameter> namedParams;
+ bool skipSyscallNumber = false;
+ if (GetFunction() && (operandList.size() > 0) && (operandList[0].operation == HLIL_CONST))
+ {
+ const auto platform = GetFunction()->GetPlatform();
+ if (platform)
+ {
+ const auto syscall = (uint32_t)operandList[0].GetConstant<HLIL_CONST>();
+ const auto syscallName = platform->GetSystemCallName(syscall);
+ if (settings && settings->GetCallParameterHints() != NeverShowParameterHints)
+ {
+ const auto functionType = platform->GetSystemCallType(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<HLIL_GOTO>();
+ 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<HLIL_LABEL>();
+ tokens.DecreaseIndent();
+ tokens.Append(GotoLabelToken, "'" + GetFunction()->GetGotoLabelName(target), target);
+ tokens.Append(TextToken, ":");
+ tokens.IncreaseIndent();
+ }();
+ break;
+
+ case HLIL_LOW_PART:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_LOW_PART>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, 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), "/* <UNIMPLEMENTED, %x> */", instr.operation);
+ tokens.Append(AnnotationToken, buf);
+ }();
+ break;
+ }
+
+ if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType())
+ {
+ tokens.AppendCloseParen();
+ }
+}
+
+
+void PseudoRustFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement)
+{
+ GetExprText(instr, tokens, settings, asFullAst, 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<LanguageRepresentationFunction> PseudoRustFunctionType::Create(Architecture* arch, Function* owner,
+ HighLevelILFunction* highLevelILFunction)
+{
+ return new PseudoRustFunction(arch, owner, highLevelILFunction);
+}
+
+
+Ref<TypePrinter> PseudoRustFunctionType::GetTypePrinter()
+{
+ // Return the Rust type printer as the default type printer for this language
+ return m_typePrinter;
+}
+
+
+vector<DisassemblyTextLine> PseudoRustFunctionType::GetFunctionTypeTokens(Function* func, DisassemblySettings* settings)
+{
+ vector<DisassemblyTextLine> result;
+ DisassemblyTextLine line;
+ line.addr = func->GetStart();
+
+ RustTypePrinter printer;
+ Ref<Type> funcType = func->GetType();
+ if (!funcType)
+ return {};
+
+ // Use the Rust type printer to generate a Rust formatted function declaration
+ vector<InstructionTextToken> before = printer.GetTypeTokensBeforeName(funcType, func->GetPlatform());
+ vector<InstructionTextToken> after = printer.GetTypeTokensAfterNameInternal(funcType, func->GetPlatform(),
+ BN_FULL_CONFIDENCE, nullptr, NoTokenEscapingType, true);
+
+ line.tokens = before;
+ if (!before.empty())
+ line.tokens.emplace_back(TextToken, " ");
+
+ Ref<Symbol> 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
+
+#ifndef DEMO_VERSION
+ BINARYNINJAPLUGIN void CorePluginDependencies()
+ {
+ }
+#endif
+
+#ifdef DEMO_VERSION
+ bool PseudoCPluginInit()
+#else
+ BINARYNINJAPLUGIN bool CorePluginInit()
+#endif
+ {
+ LanguageRepresentationFunctionType* type = new PseudoRustFunctionType();
+ LanguageRepresentationFunctionType::Register(type);
+ return true;
+ }
+}
diff --git a/lang/rust/pseudorust.h b/lang/rust/pseudorust.h
new file mode 100644
index 00000000..a6494319
--- /dev/null
+++ b/lang/rust/pseudorust.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+
+class PseudoRustFunction: public BinaryNinja::LanguageRepresentationFunction
+{
+ BinaryNinja::Ref<BinaryNinja::HighLevelILFunction> m_highLevelIL;
+
+ enum FieldDisplayType
+ {
+ FieldDisplayName,
+ FieldDisplayOffset,
+ FieldDisplayMemberOffset,
+ FieldDisplayNone
+ };
+
+ enum ExpressionType
+ {
+ StatementExpression,
+ TrailingStatementExpression,
+ InnerExpression
+ };
+
+ BinaryNinja::Ref<BinaryNinja::Type> GetFieldType(const BinaryNinja::HighLevelILInstruction& var, bool deref);
+ FieldDisplayType GetFieldDisplayType(BinaryNinja::Ref<BinaryNinja::Type> type, uint64_t offset, size_t memberIndex, bool deref);
+
+ BNSymbolDisplayResult AppendPointerTextToken(const BinaryNinja::HighLevelILInstruction& instr, int64_t val,
+ std::vector<BinaryNinja::InstructionTextToken>& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence);
+ std::string GetSizeToken(size_t size, bool isSigned);
+ void AppendSizeToken(size_t size, bool isSigned, BinaryNinja::HighLevelILTokenEmitter& emitter);
+ void AppendSingleSizeToken(size_t size, BNInstructionTextTokenType type, BinaryNinja::HighLevelILTokenEmitter& emitter);
+ void AppendComparison(const std::string& comparison, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt);
+ void AppendTwoOperand(const std::string& operand, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt);
+ void AppendTwoOperandFunction(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, bool sizeToken = true);
+ void AppendTwoOperandMethodCall(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings);
+ void AppendTwoOperandFunctionWithCarry(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings);
+ void AppendFieldTextTokens(const BinaryNinja::HighLevelILInstruction& var, uint64_t offset, size_t memberIndex, size_t size,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, bool deref);
+ bool IsMutable(const BinaryNinja::Variable& var) const;
+
+ void GetExprText(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, bool asFullAst = true,
+ BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, ExpressionType exprType = InnerExpression,
+ std::optional<bool> signedHint = std::nullopt);
+
+protected:
+ virtual void InitTokenEmitter(BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+ virtual void GetExprText(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement) override;
+ virtual void BeginLines(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+ virtual void EndLines(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+
+public:
+ PseudoRustFunction(BinaryNinja::Architecture* arch, BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction);
+
+ std::string GetAnnotationStartString() const override;
+ std::string GetAnnotationEndString() const override;
+};
+
+class PseudoRustFunctionType: public BinaryNinja::LanguageRepresentationFunctionType
+{
+ BinaryNinja::Ref<BinaryNinja::TypePrinter> m_typePrinter;
+
+public:
+ PseudoRustFunctionType();
+ BinaryNinja::Ref<BinaryNinja::LanguageRepresentationFunction> Create(BinaryNinja::Architecture* arch,
+ BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction) override;
+ BinaryNinja::Ref<BinaryNinja::TypePrinter> GetTypePrinter() override;
+ std::vector<BinaryNinja::DisassemblyTextLine> GetFunctionTypeTokens(BinaryNinja::Function* func,
+ BinaryNinja::DisassemblySettings* settings) override;
+};
diff --git a/lang/rust/rusttypes.cpp b/lang/rust/rusttypes.cpp
new file mode 100644
index 00000000..c43817ab
--- /dev/null
+++ b/lang/rust/rusttypes.cpp
@@ -0,0 +1,339 @@
+#include <inttypes.h>
+#include "rusttypes.h"
+
+using namespace std;
+using namespace BinaryNinja;
+
+
+RustTypePrinter::RustTypePrinter(): TypePrinter("RustTypePrinter")
+{
+}
+
+
+void RustTypePrinter::AppendCallingConventionTokens(Type* type, Platform* platform, uint8_t baseConfidence,
+ vector<InstructionTextToken>& tokens)
+{
+ if (type->GetCallingConvention() && platform &&
+ type->GetCallingConvention().GetValue() != platform->GetDefaultCallingConvention())
+ {
+ uint8_t ccConfidence = type->GetCallingConvention().GetCombinedConfidence(baseConfidence);
+ tokens.emplace_back(baseConfidence, KeywordToken, "extern");
+ tokens.emplace_back(ccConfidence, TextToken, " ");
+ tokens.emplace_back(ccConfidence, BraceToken, "\"");
+ if (type->GetCallingConvention().GetValue() == platform->GetCdeclCallingConvention())
+ {
+ tokens.emplace_back(StringToken, StringDisplayTokenContext, "C", 0,
+ Utf8String, 0, BN_INVALID_OPERAND, ccConfidence);
+ }
+ else
+ {
+ tokens.emplace_back(StringToken, StringDisplayTokenContext, type->GetCallingConvention()->GetName(), 0,
+ Utf8String, 0, BN_INVALID_OPERAND, ccConfidence);
+ }
+ tokens.emplace_back(ccConfidence, BraceToken, "\"");
+ tokens.emplace_back(ccConfidence, TextToken, " ");
+ }
+}
+
+
+void RustTypePrinter::GetStructureMemberTokens(Platform* platform, Type* type, uint8_t baseConfidence,
+ BNTokenEscapingType escaping, vector<InstructionTextToken>& out)
+{
+ uint64_t offset = 0;
+ vector<StructureMember> members = type->GetStructure()->GetMembers();
+ for (size_t i = 0; i < members.size(); i++)
+ {
+ if (members[i].type->GetClass() == FunctionTypeClass)
+ continue;
+ if (i != 0)
+ out.emplace_back(baseConfidence, TextToken, ", ");
+ else
+ out.emplace_back(baseConfidence, TextToken, " ");
+
+ if ((!type->GetStructure()->IsPacked()) && (members[i].type->GetAlignment() != 0) &&
+ ((offset % members[i].type->GetAlignment()) != 0))
+ offset += members[i].type->GetAlignment() - (offset % members[i].type->GetAlignment());
+
+ if (members[i].offset < offset)
+ {
+ out.emplace_back(baseConfidence, KeywordToken, "__offset");
+ out.emplace_back(baseConfidence, BraceToken, "(");
+ out.emplace_back(IntegerToken, DisassemblyTextRenderer::GetDisplayStringForInteger(
+ nullptr, UnsignedHexadecimalDisplayType, members[i].offset, 8),
+ members[i].offset, 0, BN_INVALID_OPERAND, baseConfidence);
+ out.emplace_back(baseConfidence, BraceToken, ")");
+ offset = members[i].offset;
+ }
+ else if (members[i].offset > offset)
+ {
+ char offsetStr[32];
+ snprintf(offsetStr, sizeof(offsetStr), "_%" PRIx64, offset);
+ out.emplace_back(baseConfidence, KeywordToken, "__padding");
+ out.emplace_back(baseConfidence, TextToken, " ");
+ out.emplace_back(baseConfidence, TextToken, offsetStr);
+ out.emplace_back(baseConfidence, TextToken, ": ");
+ out.emplace_back(baseConfidence, BraceToken, "[");
+ out.emplace_back(baseConfidence, KeywordToken, "u8");
+ out.emplace_back(baseConfidence, TextToken, "; ");
+ out.emplace_back(IntegerToken, DisassemblyTextRenderer::GetDisplayStringForInteger(
+ nullptr, UnsignedHexadecimalDisplayType, members[i].offset - offset, 8),
+ members[i].offset - offset, 0, BN_INVALID_OPERAND, baseConfidence);
+ out.emplace_back(baseConfidence, BraceToken, "]");
+ offset = members[i].offset;
+ }
+
+ vector<InstructionTextToken> after =
+ GetTypeTokensAfterName(members[i].type, platform, BN_FULL_CONFIDENCE, nullptr, escaping);
+
+ out.emplace_back(baseConfidence, FieldNameToken,
+ NameList::EscapeTypeName(members[i].name, escaping));
+ out.insert(out.end(), after.begin(), after.end());
+
+ if (!type->GetStructure()->IsUnion())
+ offset = members[i].offset + members[i].type->GetWidth();
+ }
+ out.emplace_back(baseConfidence, TextToken, " ");
+}
+
+
+vector<InstructionTextToken> RustTypePrinter::GetTypeTokensBeforeName(
+ Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType,
+ BNTokenEscapingType escaping)
+{
+ vector<InstructionTextToken> tokens;
+ if ((!parentType || !parentType->IsPointer()) && type->GetClass() == FunctionTypeClass)
+ {
+ AppendCallingConventionTokens(type, platform, baseConfidence, tokens);
+ tokens.emplace_back(baseConfidence, KeywordToken, "fn");
+ }
+ return tokens;
+}
+
+
+vector<InstructionTextToken> RustTypePrinter::GetTypeTokensAfterName(
+ Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType,
+ BNTokenEscapingType escaping)
+{
+ return GetTypeTokensAfterNameInternal(type, platform, baseConfidence, parentType, escaping);
+}
+
+
+vector<InstructionTextToken> RustTypePrinter::GetTypeTokensAfterNameInternal(
+ Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType,
+ BNTokenEscapingType escaping, bool functionHeader)
+{
+ vector<InstructionTextToken> tokens;
+ if (!parentType && type->GetClass() != FunctionTypeClass)
+ tokens.emplace_back(TextToken, ": ");
+
+ switch (type->GetClass())
+ {
+ case FunctionTypeClass:
+ {
+ if (parentType && parentType->IsPointer())
+ {
+ AppendCallingConventionTokens(type, platform, baseConfidence, tokens);
+ tokens.emplace_back(baseConfidence, KeywordToken, "fn");
+ }
+
+ tokens.emplace_back(baseConfidence, BraceToken, "(");
+
+ auto params = type->GetParameters();
+ for (size_t i = 0; i < params.size(); i++)
+ {
+ if (i != 0)
+ tokens.emplace_back(baseConfidence, TextToken, ", ");
+
+ vector<InstructionTextToken> paramTokens = GetTypeTokensAfterName(params[i].type, platform,
+ params[i].type.GetCombinedConfidence(baseConfidence), type, escaping);
+
+ if (functionHeader)
+ {
+ for (auto& token : paramTokens)
+ {
+ token.context = LocalVariableTokenContext;
+ token.address = params[i].location.ToIdentifier();
+ }
+ }
+
+ InstructionTextToken nameToken;
+ if (params[i].name.length() == 0)
+ {
+ nameToken = InstructionTextToken(TextToken, "_");
+ }
+ else
+ {
+ nameToken = InstructionTextToken(ArgumentNameToken, NameList::EscapeTypeName(params[i].name, escaping), i);
+ if (functionHeader)
+ {
+ nameToken.context = LocalVariableTokenContext;
+ nameToken.address = params[i].location.ToIdentifier();
+ }
+ }
+ tokens.push_back(nameToken);
+ tokens.emplace_back(TextToken, ": ");
+ tokens.insert(tokens.end(), paramTokens.begin(), paramTokens.end());
+
+ if (!params[i].defaultLocation && platform)
+ {
+ switch (params[i].location.type)
+ {
+ case RegisterVariableSourceType:
+ {
+ string registerName = platform->GetArchitecture()->GetRegisterName((uint32_t)params[i].location.storage);
+ tokens.emplace_back(TextToken, " @ ");
+ tokens.emplace_back(RegisterToken, NameList::EscapeTypeName(registerName, escaping), params[i].location.storage);
+ break;
+ }
+ case FlagVariableSourceType:
+ {
+ string flagName = platform->GetArchitecture()->GetFlagName((uint32_t)params[i].location.storage);
+ tokens.emplace_back(TextToken, " @ ");
+ tokens.emplace_back(AnnotationToken, NameList::EscapeTypeName(flagName, escaping), params[i].location.storage);
+ break;
+ }
+ case StackVariableSourceType:
+ {
+ tokens.emplace_back(TextToken, " @ ");
+ char storageStr[32];
+ snprintf(storageStr, sizeof(storageStr), "%" PRIi64, params[i].location.storage);
+ tokens.emplace_back(IntegerToken, storageStr, params[i].location.storage);
+ break;
+ }
+ }
+ }
+ }
+
+ if (type->HasVariableArguments())
+ {
+ if (params.size() != 0)
+ tokens.emplace_back(TextToken, ", ");
+ tokens.emplace_back(type->HasVariableArguments().GetCombinedConfidence(baseConfidence), TextToken, "...");
+ }
+
+ tokens.emplace_back(baseConfidence, BraceToken, ")");
+
+ if (!type->CanReturn())
+ {
+ // No return functions are part of the Rust language, marked by returning the "never" type.
+ tokens.emplace_back(baseConfidence, TextToken, " -> ");
+ tokens.emplace_back(type->CanReturn().GetCombinedConfidence(baseConfidence), TextToken, "!");
+ }
+ else if (type->GetChildType() && type->GetChildType()->GetClass() != VoidTypeClass)
+ {
+ tokens.emplace_back(baseConfidence, TextToken, " -> ");
+ vector<InstructionTextToken> retn = GetTypeTokensAfterName(type->GetChildType(), platform,
+ type->GetChildType().GetCombinedConfidence(baseConfidence), type, escaping);
+ if (functionHeader)
+ {
+ for (auto& i : retn)
+ i.context = FunctionReturnTokenContext;
+ }
+ tokens.insert(tokens.end(), retn.begin(), retn.end());
+ }
+ break;
+ }
+ case IntegerTypeClass:
+ tokens.emplace_back(baseConfidence, TypeNameToken,
+ (type->IsSigned() ? "i" : "u") + to_string(type->GetWidth() * 8));
+ break;
+ case FloatTypeClass:
+ tokens.emplace_back(baseConfidence, TypeNameToken, "f" + to_string(type->GetWidth() * 8));
+ break;
+ case WideCharTypeClass:
+ if (type->GetWidth() == 4)
+ tokens.emplace_back(baseConfidence, TypeNameToken, "char");
+ else
+ tokens.emplace_back(baseConfidence, TypeNameToken, "char" + to_string(type->GetWidth() * 8));
+ break;
+ case BoolTypeClass:
+ tokens.emplace_back(baseConfidence, TypeNameToken, "bool");
+ break;
+ case VoidTypeClass:
+ if (parentType && parentType->IsPointer())
+ {
+ tokens.emplace_back(baseConfidence, TypeNameToken, "c_void");
+ }
+ else
+ {
+ tokens.emplace_back(baseConfidence, BraceToken, "(");
+ tokens.emplace_back(baseConfidence, BraceToken, ")");
+ }
+ break;
+ case StructureTypeClass:
+ if (type->GetRegisteredName())
+ {
+ tokens.emplace_back(baseConfidence, TypeNameToken, type->GetRegisteredName()->GetName().GetString(escaping));
+ }
+ else
+ {
+ tokens.emplace_back(baseConfidence, KeywordToken, "struct");
+ tokens.emplace_back(baseConfidence, TextToken, " ");
+ tokens.emplace_back(baseConfidence, BraceToken, "{");
+ GetStructureMemberTokens(platform, type, baseConfidence, escaping, tokens);
+ tokens.emplace_back(baseConfidence, BraceToken, "}");
+ }
+ break;
+ case EnumerationTypeClass:
+ if (type->GetRegisteredName())
+ tokens.emplace_back(baseConfidence, TypeNameToken, type->GetRegisteredName()->GetName().GetString(escaping));
+ else
+ tokens.emplace_back(baseConfidence, TypeNameToken, "i" + to_string(type->GetWidth() * 8));
+ break;
+ case VarArgsTypeClass:
+ tokens.emplace_back(baseConfidence, TextToken, "...");
+ break;
+ case PointerTypeClass:
+ {
+ if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ vector<InstructionTextToken> inner = GetTypeTokensAfterName(
+ type->GetChildType(), platform, baseConfidence, type, escaping);
+ tokens.insert(tokens.end(), inner.begin(), inner.end());
+ return tokens;
+ }
+
+ tokens.emplace_back(baseConfidence, TextToken, "*");
+ if (type->GetChildType()->IsConst())
+ tokens.emplace_back(baseConfidence, KeywordToken, "const");
+ else
+ tokens.emplace_back(baseConfidence, KeywordToken, "mut");
+ tokens.emplace_back(baseConfidence, TextToken, " ");
+ vector<InstructionTextToken> inner = GetTypeTokensAfterName(type->GetChildType(), platform,
+ type->GetChildType().GetCombinedConfidence(baseConfidence), type, escaping);
+ tokens.insert(tokens.end(), inner.begin(), inner.end());
+ break;
+ }
+ case ArrayTypeClass:
+ {
+ tokens.emplace_back(baseConfidence, BraceToken, "[");
+ vector<InstructionTextToken> inner = GetTypeTokensAfterName(type->GetChildType(), platform,
+ type->GetChildType().GetCombinedConfidence(baseConfidence), type, escaping);
+ tokens.insert(tokens.end(), inner.begin(), inner.end());
+ tokens.emplace_back(baseConfidence, TextToken, "; ");
+ tokens.emplace_back(ArrayIndexToken, DisassemblyTextRenderer::GetDisplayStringForInteger(
+ nullptr, type->GetIntegerTypeDisplayType(), type->GetElementCount(), 8).c_str(), type->GetElementCount());
+ tokens.emplace_back(baseConfidence, BraceToken, "]");
+ }
+ case ValueTypeClass:
+ tokens.emplace_back(baseConfidence, TextToken, NameList::EscapeTypeName(type->GetAlternateName(), escaping));
+ break;
+ case NamedTypeReferenceClass:
+ tokens.emplace_back(baseConfidence, TypeNameToken, type->GetNamedTypeReference()->GetName().GetString(escaping));
+ break;
+ default:
+ tokens.emplace_back(AnnotationToken, "/* invalid type */");
+ break;
+ }
+
+ return tokens;
+}
+
+
+vector<TypeDefinitionLine> RustTypePrinter::GetTypeLines(
+ Ref<Type> type, const TypeContainer& types, const QualifiedName& name, int paddingCols,
+ bool collapsed, BNTokenEscapingType escaping)
+{
+ // TODO: Implement this to get type rendering in the Types view
+ return {};
+}
diff --git a/lang/rust/rusttypes.h b/lang/rust/rusttypes.h
new file mode 100644
index 00000000..419e5253
--- /dev/null
+++ b/lang/rust/rusttypes.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+
+class RustTypePrinter: public BinaryNinja::TypePrinter
+{
+ void AppendCallingConventionTokens(BinaryNinja::Type* type, BinaryNinja::Platform* platform, uint8_t baseConfidence,
+ std::vector<BinaryNinja::InstructionTextToken>& tokens);
+ void GetStructureMemberTokens(BinaryNinja::Platform* platform, BinaryNinja::Type* type, uint8_t baseConfidence,
+ BNTokenEscapingType escaping, std::vector<BinaryNinja::InstructionTextToken>& out);
+
+public:
+ RustTypePrinter();
+
+ std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensBeforeName(
+ BinaryNinja::Ref<BinaryNinja::Type> type, BinaryNinja::Ref<BinaryNinja::Platform> platform,
+ uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr,
+ BNTokenEscapingType escaping = NoTokenEscapingType) override;
+ std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensAfterName(
+ BinaryNinja::Ref<BinaryNinja::Type> type, BinaryNinja::Ref<BinaryNinja::Platform> platform,
+ uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr,
+ BNTokenEscapingType escaping = NoTokenEscapingType) override;
+ std::vector<BinaryNinja::TypeDefinitionLine> GetTypeLines(
+ BinaryNinja::Ref<BinaryNinja::Type> type, const BinaryNinja::TypeContainer& types,
+ const BinaryNinja::QualifiedName& name, int paddingCols = 64, bool collapsed = false,
+ BNTokenEscapingType escaping = NoTokenEscapingType) override;
+
+ std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensAfterNameInternal(
+ BinaryNinja::Ref<BinaryNinja::Type> type, BinaryNinja::Ref<BinaryNinja::Platform> platform,
+ uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr,
+ BNTokenEscapingType escaping = NoTokenEscapingType, bool functionHeader = false);
+};
diff --git a/languagerepresentation.cpp b/languagerepresentation.cpp
index 62f592e8..906b4901 100644
--- a/languagerepresentation.cpp
+++ b/languagerepresentation.cpp
@@ -1,15 +1,493 @@
#include "binaryninjaapi.h"
+#include "highlevelilinstruction.h"
using namespace BinaryNinja;
using namespace std;
-LanguageRepresentationFunction::LanguageRepresentationFunction(Architecture* arch, Function* func)
+LanguageRepresentationFunction::LanguageRepresentationFunction(Architecture* arch, Function* func, HighLevelILFunction* highLevelIL)
{
- m_object = BNCreateLanguageRepresentationFunction(arch->GetObject(), func ? func->GetObject() : nullptr);
+ BNCustomLanguageRepresentationFunction callbacks;
+ callbacks.context = this;
+ callbacks.freeObject = FreeCallback;
+ callbacks.externalRefTaken = nullptr;
+ callbacks.externalRefReleased = nullptr;
+ callbacks.initTokenEmitter = InitTokenEmitterCallback;
+ callbacks.getExprText = GetExprTextCallback;
+ callbacks.beginLines = BeginLinesCallback;
+ callbacks.endLines = EndLinesCallback;
+ callbacks.getCommentStartString = GetCommentStartStringCallback;
+ callbacks.getCommentEndString = GetCommentEndStringCallback;
+ callbacks.getAnnotationStartString = GetAnnotationStartStringCallback;
+ callbacks.getAnnotationEndString = GetAnnotationEndStringCallback;
+ AddRefForRegistration();
+ m_object = BNCreateCustomLanguageRepresentationFunction(arch->GetObject(), func->GetObject(),
+ highLevelIL->GetObject(), &callbacks);
}
LanguageRepresentationFunction::LanguageRepresentationFunction(BNLanguageRepresentationFunction* func)
{
m_object = func;
-} \ No newline at end of file
+}
+
+
+vector<DisassemblyTextLine> LanguageRepresentationFunction::GetExprText(const HighLevelILInstruction& instr,
+ DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement)
+{
+ size_t count = 0;
+ BNDisassemblyTextLine* lines = BNGetLanguageRepresentationFunctionExprText(m_object,
+ instr.function->GetObject(), instr.exprIndex, settings ? settings->GetObject() : nullptr,
+ asFullAst, precedence, statement, &count);
+
+ vector<DisassemblyTextLine> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ {
+ DisassemblyTextLine line;
+ line.addr = lines[i].addr;
+ line.instrIndex = lines[i].instrIndex;
+ line.highlight = lines[i].highlight;
+ line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count);
+ line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount);
+ result.push_back(line);
+ }
+
+ BNFreeDisassemblyTextLines(lines, count);
+ return result;
+}
+
+
+vector<DisassemblyTextLine> LanguageRepresentationFunction::GetLinearLines(
+ const HighLevelILInstruction& instr, DisassemblySettings* settings, bool asFullAst)
+{
+ size_t count = 0;
+ BNDisassemblyTextLine* lines = BNGetLanguageRepresentationFunctionLinearLines(m_object, instr.function->GetObject(),
+ instr.exprIndex, settings ? settings->GetObject() : nullptr, asFullAst, &count);
+
+ vector<DisassemblyTextLine> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ {
+ DisassemblyTextLine line;
+ line.addr = lines[i].addr;
+ line.instrIndex = lines[i].instrIndex;
+ line.highlight = lines[i].highlight;
+ line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count);
+ line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount);
+ result.push_back(line);
+ }
+
+ BNFreeDisassemblyTextLines(lines, count);
+ return result;
+}
+
+
+vector<DisassemblyTextLine> LanguageRepresentationFunction::GetBlockLines(
+ BasicBlock* block, DisassemblySettings* settings)
+{
+ size_t count = 0;
+ BNDisassemblyTextLine* lines = BNGetLanguageRepresentationFunctionBlockLines(
+ m_object, block->GetObject(), settings ? settings->GetObject() : nullptr, &count);
+
+ vector<DisassemblyTextLine> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ {
+ DisassemblyTextLine line;
+ line.addr = lines[i].addr;
+ line.instrIndex = lines[i].instrIndex;
+ line.highlight = lines[i].highlight;
+ line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count);
+ line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount);
+ result.push_back(line);
+ }
+
+ BNFreeDisassemblyTextLines(lines, count);
+ return result;
+}
+
+
+BNHighlightColor LanguageRepresentationFunction::GetHighlight(BasicBlock* block)
+{
+ return BNGetLanguageRepresentationFunctionHighlight(m_object, block->GetObject());
+}
+
+
+Ref<Architecture> LanguageRepresentationFunction::GetArchitecture() const
+{
+ return new CoreArchitecture(BNGetLanguageRepresentationArchitecture(m_object));
+}
+
+
+Ref<Function> LanguageRepresentationFunction::GetFunction() const
+{
+ return new Function(BNGetLanguageRepresentationOwnerFunction(m_object));
+}
+
+
+Ref<HighLevelILFunction> LanguageRepresentationFunction::GetHighLevelILFunction() const
+{
+ return new HighLevelILFunction(BNGetLanguageRepresentationILFunction(m_object));
+}
+
+
+void LanguageRepresentationFunction::InitTokenEmitter(HighLevelILTokenEmitter&)
+{
+}
+
+
+void LanguageRepresentationFunction::BeginLines(const HighLevelILInstruction&, HighLevelILTokenEmitter&)
+{
+}
+
+
+void LanguageRepresentationFunction::EndLines(const HighLevelILInstruction&, HighLevelILTokenEmitter&)
+{
+}
+
+
+void LanguageRepresentationFunction::FreeCallback(void* ctxt)
+{
+ LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt;
+ func->ReleaseForRegistration();
+}
+
+
+void LanguageRepresentationFunction::InitTokenEmitterCallback(void* ctxt, BNHighLevelILTokenEmitter* tokens)
+{
+ LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt;
+ Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens));
+ func->InitTokenEmitter(*tokenObj);
+}
+
+
+void LanguageRepresentationFunction::GetExprTextCallback(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex,
+ BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, bool statement)
+{
+ LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt;
+ Ref<HighLevelILFunction> ilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(il));
+ HighLevelILInstruction instr = ilObj->GetExpr(exprIndex);
+ Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens));
+ Ref<DisassemblySettings> settingsObj = settings ? new DisassemblySettings(BNNewDisassemblySettingsReference(settings)) : nullptr;
+ func->GetExprText(instr, *tokenObj, settingsObj, asFullAst, precedence, statement);
+}
+
+
+void LanguageRepresentationFunction::BeginLinesCallback(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex,
+ BNHighLevelILTokenEmitter* tokens)
+{
+ LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt;
+ Ref<HighLevelILFunction> ilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(il));
+ HighLevelILInstruction instr = ilObj->GetExpr(exprIndex);
+ Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens));
+ func->BeginLines(instr, *tokenObj);
+}
+
+
+void LanguageRepresentationFunction::EndLinesCallback(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex,
+ BNHighLevelILTokenEmitter* tokens)
+{
+ LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt;
+ Ref<HighLevelILFunction> ilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(il));
+ HighLevelILInstruction instr = ilObj->GetExpr(exprIndex);
+ Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens));
+ func->EndLines(instr, *tokenObj);
+}
+
+
+char* LanguageRepresentationFunction::GetCommentStartStringCallback(void* ctxt)
+{
+ LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt;
+ return BNAllocString(func->GetCommentStartString().c_str());
+}
+
+
+char* LanguageRepresentationFunction::GetCommentEndStringCallback(void* ctxt)
+{
+ LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt;
+ return BNAllocString(func->GetCommentEndString().c_str());
+}
+
+
+char* LanguageRepresentationFunction::GetAnnotationStartStringCallback(void* ctxt)
+{
+ LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt;
+ return BNAllocString(func->GetAnnotationStartString().c_str());
+}
+
+
+char* LanguageRepresentationFunction::GetAnnotationEndStringCallback(void* ctxt)
+{
+ LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt;
+ return BNAllocString(func->GetAnnotationEndString().c_str());
+}
+
+
+CoreLanguageRepresentationFunction::CoreLanguageRepresentationFunction(BNLanguageRepresentationFunction* func):
+ LanguageRepresentationFunction(func)
+{
+}
+
+
+void CoreLanguageRepresentationFunction::GetExprText(const HighLevelILInstruction&, HighLevelILTokenEmitter&,
+ DisassemblySettings*, bool, BNOperatorPrecedence, bool statement)
+{
+}
+
+
+string CoreLanguageRepresentationFunction::GetCommentStartString() const
+{
+ char* result = BNGetLanguageRepresentationFunctionCommentStartString(m_object);
+ string resultStr(result);
+ BNFreeString(result);
+ return resultStr;
+}
+
+
+string CoreLanguageRepresentationFunction::GetCommentEndString() const
+{
+ char* result = BNGetLanguageRepresentationFunctionCommentEndString(m_object);
+ string resultStr(result);
+ BNFreeString(result);
+ return resultStr;
+}
+
+
+string CoreLanguageRepresentationFunction::GetAnnotationStartString() const
+{
+ char* result = BNGetLanguageRepresentationFunctionAnnotationStartString(m_object);
+ string resultStr(result);
+ BNFreeString(result);
+ return resultStr;
+}
+
+
+string CoreLanguageRepresentationFunction::GetAnnotationEndString() const
+{
+ char* result = BNGetLanguageRepresentationFunctionAnnotationEndString(m_object);
+ string resultStr(result);
+ BNFreeString(result);
+ return resultStr;
+}
+
+
+LanguageRepresentationFunctionType::LanguageRepresentationFunctionType(const std::string& name): m_nameForRegister(name)
+{
+ m_object = nullptr;
+}
+
+
+LanguageRepresentationFunctionType::LanguageRepresentationFunctionType(BNLanguageRepresentationFunctionType* type)
+{
+ m_object = type;
+}
+
+
+string LanguageRepresentationFunctionType::GetName() const
+{
+ char* name = BNGetLanguageRepresentationFunctionTypeName(m_object);
+ string result = name;
+ BNFreeString(name);
+ return result;
+}
+
+
+bool LanguageRepresentationFunctionType::IsValid(BinaryView*)
+{
+ return true;
+}
+
+
+vector<DisassemblyTextLine> LanguageRepresentationFunctionType::GetFunctionTypeTokens(Function*, DisassemblySettings*)
+{
+ return {};
+}
+
+
+void LanguageRepresentationFunctionType::Register(LanguageRepresentationFunctionType* type)
+{
+ BNCustomLanguageRepresentationFunctionType callbacks;
+ callbacks.context = type;
+ callbacks.create = CreateCallback;
+ callbacks.isValid = IsValidCallback;
+ callbacks.getTypePrinter = GetTypePrinterCallback;
+ callbacks.getTypeParser = GetTypeParserCallback;
+ callbacks.getFunctionTypeTokens = GetFunctionTypeTokensCallback;
+ callbacks.freeLines = FreeLinesCallback;
+
+ type->AddRefForRegistration();
+ type->m_object =
+ BNRegisterLanguageRepresentationFunctionType(type->m_nameForRegister.c_str(), &callbacks);
+}
+
+
+BNLanguageRepresentationFunction* LanguageRepresentationFunctionType::CreateCallback(
+ void* ctxt, BNArchitecture* arch, BNFunction* owner, BNHighLevelILFunction* highLevelIL)
+{
+ LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt;
+ Ref<Architecture> archObj = new CoreArchitecture(arch);
+ Ref<Function> ownerObj = new Function(BNNewFunctionReference(owner));
+ Ref<HighLevelILFunction> il = new HighLevelILFunction(BNNewHighLevelILFunctionReference(highLevelIL));
+ Ref<LanguageRepresentationFunction> result = type->Create(archObj, ownerObj, il);
+ if (!result)
+ return nullptr;
+ return BNNewLanguageRepresentationFunctionReference(result->GetObject());
+}
+
+
+bool LanguageRepresentationFunctionType::IsValidCallback(void* ctxt, BNBinaryView* view)
+{
+ LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt;
+ Ref<BinaryView> viewObj = new BinaryView(BNNewViewReference(view));
+ return type->IsValid(viewObj);
+}
+
+
+BNTypePrinter* LanguageRepresentationFunctionType::GetTypePrinterCallback(void* ctxt)
+{
+ LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt;
+ Ref<TypePrinter> result = type->GetTypePrinter();
+ if (!result)
+ return nullptr;
+ return result->GetObject();
+}
+
+
+BNTypeParser* LanguageRepresentationFunctionType::GetTypeParserCallback(void* ctxt)
+{
+ LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt;
+ Ref<TypeParser> result = type->GetTypeParser();
+ if (!result)
+ return nullptr;
+ return result->GetObject();
+}
+
+
+BNDisassemblyTextLine* LanguageRepresentationFunctionType::GetFunctionTypeTokensCallback(
+ void* ctxt, BNFunction* func, BNDisassemblySettings* settings, size_t* count)
+{
+ LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt;
+ Ref<Function> funcObj = new Function(BNNewFunctionReference(func));
+ Ref<DisassemblySettings> settingsObj = settings ? new DisassemblySettings(BNNewDisassemblySettingsReference(settings)) : nullptr;
+ auto lines = type->GetFunctionTypeTokens(funcObj, settingsObj);
+ *count = lines.size();
+ BNDisassemblyTextLine* buf = new BNDisassemblyTextLine[lines.size()];
+ for (size_t i = 0; i < lines.size(); i++)
+ {
+ const DisassemblyTextLine& line = lines[i];
+ buf[i].addr = line.addr;
+ buf[i].instrIndex = line.instrIndex;
+ buf[i].highlight = line.highlight;
+ buf[i].tokens = InstructionTextToken::CreateInstructionTextTokenList(line.tokens);
+ buf[i].count = line.tokens.size();
+ buf[i].tags = Tag::CreateTagList(line.tags, &(buf[i].tagCount));
+ }
+
+ return buf;
+}
+
+
+void LanguageRepresentationFunctionType::FreeLinesCallback(void*, BNDisassemblyTextLine* lines, size_t)
+{
+ delete[] lines;
+}
+
+
+Ref<LanguageRepresentationFunctionType> LanguageRepresentationFunctionType::GetByName(const std::string& name)
+{
+ BNLanguageRepresentationFunctionType* type = BNGetLanguageRepresentationFunctionTypeByName(name.c_str());
+ if (!type)
+ return nullptr;
+ return new CoreLanguageRepresentationFunctionType(type);
+}
+
+
+bool LanguageRepresentationFunctionType::IsValidByName(const std::string& name, BinaryView* view)
+{
+ Ref<LanguageRepresentationFunctionType> type = GetByName(name);
+ if (!type)
+ return false;
+ return type->IsValid(view);
+}
+
+
+vector<Ref<LanguageRepresentationFunctionType>> LanguageRepresentationFunctionType::GetTypes()
+{
+ size_t count = 0;
+ BNLanguageRepresentationFunctionType** types = BNGetLanguageRepresentationFunctionTypeList(&count);
+
+ vector<Ref<LanguageRepresentationFunctionType>> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.push_back(new CoreLanguageRepresentationFunctionType(types[i]));
+
+ BNFreeLanguageRepresentationFunctionTypeList(types);
+ return result;
+}
+
+
+CoreLanguageRepresentationFunctionType::CoreLanguageRepresentationFunctionType(BNLanguageRepresentationFunctionType* type):
+ LanguageRepresentationFunctionType(type)
+{
+}
+
+
+Ref<LanguageRepresentationFunction> CoreLanguageRepresentationFunctionType::Create(
+ Architecture* arch, Function* owner, HighLevelILFunction* highLevelIL)
+{
+ BNLanguageRepresentationFunction* func = BNCreateLanguageRepresentationFunction(
+ m_object, arch->GetObject(), owner->GetObject(), highLevelIL->GetObject());
+ if (!func)
+ return nullptr;
+ return new CoreLanguageRepresentationFunction(func);
+}
+
+
+bool CoreLanguageRepresentationFunctionType::IsValid(BinaryView* view)
+{
+ return BNIsLanguageRepresentationFunctionTypeValid(m_object, view->GetObject());
+}
+
+
+Ref<TypePrinter> CoreLanguageRepresentationFunctionType::GetTypePrinter()
+{
+ BNTypePrinter* printer = BNGetLanguageRepresentationFunctionTypePrinter(m_object);
+ if (!printer)
+ return nullptr;
+ return new CoreTypePrinter(printer);
+}
+
+
+Ref<TypeParser> CoreLanguageRepresentationFunctionType::GetTypeParser()
+{
+ BNTypeParser* parser = BNGetLanguageRepresentationFunctionTypeParser(m_object);
+ if (!parser)
+ return nullptr;
+ return new CoreTypeParser(parser);
+}
+
+
+vector<DisassemblyTextLine> CoreLanguageRepresentationFunctionType::GetFunctionTypeTokens(
+ Function* func, DisassemblySettings* settings)
+{
+ size_t count = 0;
+ BNDisassemblyTextLine* lines = BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens(m_object,
+ func->GetObject(), settings ? settings->GetObject() : nullptr, &count);
+
+ vector<DisassemblyTextLine> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ {
+ DisassemblyTextLine line;
+ line.addr = lines[i].addr;
+ line.instrIndex = lines[i].instrIndex;
+ line.highlight = lines[i].highlight;
+ line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count);
+ line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount);
+ result.push_back(line);
+ }
+
+ BNFreeDisassemblyTextLines(lines, count);
+ return result;
+}
diff --git a/linearviewobject.cpp b/linearviewobject.cpp
index 3bd27b5a..4f00adb0 100644
--- a/linearviewobject.cpp
+++ b/linearviewobject.cpp
@@ -273,10 +273,11 @@ Ref<LinearViewObject> LinearViewObject::CreateHighLevelILSSAForm(BinaryView* vie
}
-Ref<LinearViewObject> LinearViewObject::CreateLanguageRepresentation(BinaryView* view, DisassemblySettings* settings)
+Ref<LinearViewObject> LinearViewObject::CreateLanguageRepresentation(BinaryView* view, DisassemblySettings* settings,
+ const string& language)
{
return new LinearViewObject(
- BNCreateLinearViewLanguageRepresentation(view->GetObject(), settings ? settings->GetObject() : nullptr));
+ BNCreateLinearViewLanguageRepresentation(view->GetObject(), settings ? settings->GetObject() : nullptr, language.c_str()));
}
@@ -363,8 +364,8 @@ Ref<LinearViewObject> LinearViewObject::CreateSingleFunctionHighLevelILSSAForm(
Ref<LinearViewObject> LinearViewObject::CreateSingleFunctionLanguageRepresentation(
- Function* func, DisassemblySettings* settings)
+ Function* func, DisassemblySettings* settings, const string& language)
{
return new LinearViewObject(BNCreateLinearViewSingleFunctionLanguageRepresentation(
- func->GetObject(), settings ? settings->GetObject() : nullptr));
+ func->GetObject(), settings ? settings->GetObject() : nullptr, language.c_str()));
}
diff --git a/python/__init__.py b/python/__init__.py
index 84494296..b6a3de97 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -79,6 +79,7 @@ from .debuginfo import *
from .externallibrary import *
from .undo import *
from .fileaccessor import *
+from .languagerepresentation import *
# We import each of these by name to prevent conflicts between
# log.py and the function 'log' which we don't import below
from .log import (
diff --git a/python/binaryview.py b/python/binaryview.py
index fe270c9a..6adb8b1e 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -8545,7 +8545,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
def find_next_text(
self, start: int, text: str, settings: Optional[_function.DisassemblySettings] = None,
flags: FindFlag = FindFlag.FindCaseSensitive,
- graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph
+ graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph
) -> Optional[int]:
"""
``find_next_text`` searches for string ``text`` occurring in the linear view output starting at the virtual
@@ -8562,7 +8562,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
FindCaseSensitive Case-sensitive search
FindCaseInsensitive Case-insensitive search
==================== ============================
- :param FunctionGraphType graph_type: the IL to search within
+ :param FunctionViewType graph_type: the IL to search within
"""
if not isinstance(text, str):
raise TypeError("text parameter is not str type")
@@ -8572,13 +8572,14 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
raise TypeError("settings parameter is not DisassemblySettings type")
result = ctypes.c_ulonglong()
+ graph_type = _function.FunctionViewType(graph_type)._to_core_struct()
if not core.BNFindNextText(self.handle, start, text, result, settings.handle, flags, graph_type):
return None
return result.value
def find_next_constant(
self, start: int, constant: int, settings: Optional[_function.DisassemblySettings] = None,
- graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph
+ graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph
) -> Optional[int]:
"""
``find_next_constant`` searches for integer constant ``constant`` occurring in the linear view output starting at the virtual
@@ -8587,7 +8588,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
:param int start: virtual address to start searching from.
:param int constant: constant to search for
:param DisassemblySettings settings: disassembly settings
- :param FunctionGraphType graph_type: the IL to search within
+ :param FunctionViewType graph_type: the IL to search within
"""
if not isinstance(constant, int):
raise TypeError("constant parameter is not integral type")
@@ -8597,6 +8598,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
raise TypeError("settings parameter is not DisassemblySettings type")
result = ctypes.c_ulonglong()
+ graph_type = _function.FunctionViewType(graph_type)._to_core_struct()
if not core.BNFindNextConstant(self.handle, start, constant, result, settings.handle, graph_type):
return None
return result.value
@@ -8706,7 +8708,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
def find_all_text(
self, start: int, end: int, text: str, settings: Optional[_function.DisassemblySettings] = None,
- flags=FindFlag.FindCaseSensitive, graph_type=FunctionGraphType.NormalFunctionGraph, progress_func=None,
+ flags=FindFlag.FindCaseSensitive, graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph, progress_func=None,
match_callback=None
) -> QueueGenerator:
"""
@@ -8727,7 +8729,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
FindCaseSensitive Case-sensitive search
FindCaseInsensitive Case-insensitive search
==================== ============================
- :param FunctionGraphType graph_type: the IL to search within
+ :param FunctionViewType graph_type: the IL to search within
:param callback progress_func: optional function to be called with the current progress \
and total count. This function should return a boolean value that decides whether the \
search should continue or stop
@@ -8752,6 +8754,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
raise TypeError("settings parameter is not DisassemblySettings type")
if not isinstance(flags, FindFlag):
raise TypeError('flag parameter must have type FindFlag')
+ graph_type = _function.FunctionViewType(graph_type)._to_core_struct()
if progress_func:
progress_func_obj = ctypes.CFUNCTYPE(
@@ -8799,7 +8802,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
def find_all_constant(
self, start: int, end: int, constant: int, settings: Optional[_function.DisassemblySettings] = None,
- graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph, progress_func: Optional[ProgressFuncType] = None,
+ graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph, progress_func: Optional[ProgressFuncType] = None,
match_callback: Optional[LineMatchCallbackType] = None
) -> QueueGenerator:
"""
@@ -8817,7 +8820,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
:param int constant: constant to search for
:param DisassemblySettings settings: DisassemblySettings object used to render the text \
to be searched
- :param FunctionGraphType graph_type: the IL to search within
+ :param FunctionViewType graph_type: the IL to search within
:param callback progress_func: optional function to be called with the current progress \
and total count. This function should return a boolean value that decides whether the \
search should continue or stop
@@ -8839,6 +8842,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
settings.set_option(DisassemblyOption.WaitForIL, True)
if not isinstance(settings, _function.DisassemblySettings):
raise TypeError("settings parameter is not DisassemblySettings type")
+ graph_type = _function.FunctionViewType(graph_type)._to_core_struct()
if progress_func:
progress_func_obj = ctypes.CFUNCTYPE(
@@ -9628,6 +9632,21 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
def memory_map(self):
return MemoryMap(handle=self.handle)
+ def stringify_unicode_data(
+ self, arch: Optional['architecture.Architecture'], buffer: 'databuffer.DataBuffer',
+ allow_short_strings: bool = False
+ ) -> Tuple[Optional[str], Optional[StringType]]:
+ string = ctypes.c_char_p()
+ string_type = ctypes.c_int()
+ if arch is not None:
+ arch = arch.handle
+ if not core.BNStringifyUnicodeData(
+ self.handle, arch, buffer.handle, allow_short_strings, ctypes.byref(string), ctypes.byref(string_type)):
+ return None, None
+ result = string.value
+ core.BNFreeString(string)
+ return result, StringType(string_type.value)
+
class BinaryReader:
"""
``class BinaryReader`` is a convenience class for reading binary data.
diff --git a/python/databuffer.py b/python/databuffer.py
index ce8a68e7..36cf4430 100644
--- a/python/databuffer.py
+++ b/python/databuffer.py
@@ -143,8 +143,8 @@ class DataBuffer:
return False
return bytes(self) == bytes(other)
- def escape(self, null_terminates=False) -> str:
- return core.BNDataBufferToEscapedString(self.handle, null_terminates)
+ def escape(self, null_terminates=False, escape_printable=False) -> str:
+ return core.BNDataBufferToEscapedString(self.handle, null_terminates, escape_printable)
def unescape(self) -> 'DataBuffer':
return DataBuffer(handle=core.BNDecodeEscapedString(str(self)))
diff --git a/python/examples/pseudo_python.py b/python/examples/pseudo_python.py
new file mode 100644
index 00000000..b68b4786
--- /dev/null
+++ b/python/examples/pseudo_python.py
@@ -0,0 +1,1100 @@
+# Copyright (c) 2024 Vector 35 Inc
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+from binaryninja import (Architecture, BraceRequirement, DisassemblySettings, DisassemblyTextLine, Function,
+ InstructionTextToken, InstructionTextTokenType, LanguageRepresentationFunction,
+ LanguageRepresentationFunctionType, HighLevelILInstruction, HighLevelILFunction,
+ HighLevelILTokenEmitter, HighLevelILOperation, OperatorPrecedence, ScopeType,
+ SymbolDisplayType, SymbolDisplayResult, SymbolType, BoolType, VoidType, PointerType,
+ NamedTypeReferenceType, StructureType, InstructionTextTokenContext, StructureMember,
+ BinaryView, BuiltinType)
+from typing import Optional
+import struct
+
+
+class PseudoPythonFunction(LanguageRepresentationFunction):
+ comment_start_string = "# "
+
+ def perform_init_token_emitter(self, emitter):
+ # Python never allows braces, even if the user has set the option to show them
+ emitter.brace_requirement = BraceRequirement.BracesNotAllowed
+
+ def perform_begin_lines(self, instr: HighLevelILInstruction, tokens: HighLevelILTokenEmitter):
+ # Ensure that the function body is indented relative to the function declaration
+ tokens.increase_indent()
+
+ def perform_get_expr_text(
+ self, instr: HighLevelILInstruction, tokens: HighLevelILTokenEmitter, settings: Optional[DisassemblySettings],
+ as_full_ast: bool = True, precedence: OperatorPrecedence = OperatorPrecedence.TopLevelOperatorPrecedence,
+ statement: bool = False
+ ):
+ with tokens.expr(instr):
+ if instr.operation == HighLevelILOperation.HLIL_BLOCK:
+ need_separator = False
+ body = instr.body
+ # Emit the lines for each statement in the body
+ for (idx, i) in enumerate(body):
+ # Don't display trailing return statements that don't have values
+ if (as_full_ast and idx + 1 == len(body) and i.operation == HighLevelILOperation.HLIL_RET and
+ len(i.src) == 0 and instr.expr_index == self.hlil.root.expr_index):
+ continue
+
+ # If the statement is one that contains additional blocks of code, insert a scope separator
+ # to visually separate the logic.
+ has_blocks = i.operation in [HighLevelILOperation.HLIL_IF, HighLevelILOperation.HLIL_WHILE,
+ HighLevelILOperation.HLIL_DO_WHILE, HighLevelILOperation.HLIL_FOR,
+ HighLevelILOperation.HLIL_SWITCH]
+ if need_separator or (idx != 0 and has_blocks):
+ tokens.scope_separator()
+ need_separator = has_blocks
+
+ # Emit the lines for the statement itself
+ self.perform_get_expr_text(i, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.new_line()
+ elif instr.operation == HighLevelILOperation.HLIL_IF:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "if "))
+ self.perform_get_expr_text(instr.condition, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":\n"))
+ if as_full_ast:
+ # Only display the if body when printing the full AST. When printing basic blocks in graph view,
+ # the body of the if and the else part are rendered as other nodes in the graph.
+ tokens.begin_scope(ScopeType.BlockScopeType)
+ self.perform_get_expr_text(instr.true, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.end_scope(ScopeType.BlockScopeType)
+
+ # Else statements need to be handled as chains, since "else if" in Python should be rendered
+ # as "elif" statements.
+ if_chain = instr.false
+ while if_chain is not None and if_chain.operation not in [HighLevelILOperation.HLIL_NOP,
+ HighLevelILOperation.HLIL_UNREACHABLE]:
+ if if_chain.operation == HighLevelILOperation.HLIL_IF:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "elif "))
+ self.perform_get_expr_text(if_chain.condition, tokens, settings, as_full_ast)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":"))
+ tokens.begin_scope(ScopeType.BlockScopeType)
+ self.perform_get_expr_text(if_chain.true, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.end_scope(ScopeType.BlockScopeType)
+ if_chain = if_chain.false
+ else:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "else"))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":"))
+ tokens.begin_scope(ScopeType.BlockScopeType)
+ self.perform_get_expr_text(if_chain, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.end_scope(ScopeType.BlockScopeType)
+ break
+ elif instr.operation == HighLevelILOperation.HLIL_FOR:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "for "))
+
+ # If the for loop can be represented as a Python range function, show it that way
+ dest_var = None
+ if instr.init.operation == HighLevelILOperation.HLIL_VAR_INIT:
+ dest_var = instr.init.dest
+ elif instr.init.operation == HighLevelILOperation.HLIL_ASSIGN and instr.init.dest.operation == HighLevelILOperation.HLIL_VAR:
+ dest_var = instr.init.dest.var
+ if (dest_var is not None and
+ (instr.condition.operation in [HighLevelILOperation.HLIL_CMP_SLT,
+ HighLevelILOperation.HLIL_CMP_ULT]) and
+ instr.condition.left.operation == HighLevelILOperation.HLIL_VAR and
+ instr.condition.left.var == dest_var and
+ instr.update.operation == HighLevelILOperation.HLIL_ASSIGN and
+ instr.update.dest.operation == HighLevelILOperation.HLIL_VAR and
+ instr.update.dest.var == instr.condition.left.var and
+ instr.update.src.operation == HighLevelILOperation.HLIL_ADD and
+ instr.update.src.left.operation == HighLevelILOperation.HLIL_VAR and
+ instr.update.src.left.var == instr.condition.left.var):
+ step_by = (instr.update.src.right.operation != HighLevelILOperation.HLIL_CONST or
+ instr.update.src.right.constant != 1)
+ start_required = (instr.init.src.operation != HighLevelILOperation.HLIL_CONST or
+ instr.init.src.constant != 0 or step_by)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.LocalVariableToken, dest_var.name,
+ context=InstructionTextTokenContext.LocalVariableTokenContext,
+ address=instr.expr_index, value=dest_var.identifier,
+ size=instr.size))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, " in "))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "range"))
+ tokens.append_open_paren()
+ if start_required:
+ self.perform_get_expr_text(instr.init.src, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.condition.right, tokens, settings)
+ if step_by:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.update.src.right, tokens, settings)
+ tokens.append_close_paren()
+ else:
+ # Not representable directly as a Python loop, emit it with a more HLIL-like syntax
+ if instr.init.operation != HighLevelILOperation.HLIL_NOP:
+ self.perform_get_expr_text(instr.init, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "; "))
+ if instr.condition.operation != HighLevelILOperation.HLIL_NOP:
+ self.perform_get_expr_text(instr.condition, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "; "))
+ if instr.update.operation != HighLevelILOperation.HLIL_NOP:
+ self.perform_get_expr_text(instr.update, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":"))
+ tokens.begin_scope(ScopeType.BlockScopeType)
+ self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.end_scope(ScopeType.BlockScopeType)
+ elif instr.operation == HighLevelILOperation.HLIL_WHILE:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "while "))
+ self.perform_get_expr_text(instr.condition, tokens, settings, as_full_ast)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":"))
+ tokens.begin_scope(ScopeType.BlockScopeType)
+ self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.end_scope(ScopeType.BlockScopeType)
+ elif instr.operation == HighLevelILOperation.HLIL_DO_WHILE:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "do"))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":"))
+ tokens.begin_scope(ScopeType.BlockScopeType)
+ self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.end_scope(ScopeType.BlockScopeType)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "while "))
+ self.perform_get_expr_text(instr.condition, tokens, settings, as_full_ast)
+ elif instr.operation == HighLevelILOperation.HLIL_SWITCH:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "match "))
+ self.perform_get_expr_text(instr.condition, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":"))
+ tokens.begin_scope(ScopeType.SwitchScopeType)
+
+ # Output each case
+ for case in instr.cases:
+ self.perform_get_expr_text(case, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.new_line()
+
+ # Check for default case
+ if instr.default is not None and instr.default.operation not in [HighLevelILOperation.HLIL_NOP,
+ HighLevelILOperation.HLIL_UNREACHABLE]:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "default"))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":"))
+ tokens.begin_scope(ScopeType.CaseScopeType)
+ self.perform_get_expr_text(instr.default, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.end_scope(ScopeType.CaseScopeType)
+ tokens.end_scope(ScopeType.SwitchScopeType)
+ elif instr.operation == HighLevelILOperation.HLIL_CASE:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "case "))
+ for (i, value) in enumerate(instr.values):
+ if i > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " | "))
+ self.perform_get_expr_text(value, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":"))
+ tokens.begin_scope(ScopeType.CaseScopeType)
+ self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast,
+ OperatorPrecedence.TopLevelOperatorPrecedence, True)
+ tokens.end_scope(ScopeType.CaseScopeType)
+ elif instr.operation == HighLevelILOperation.HLIL_BREAK:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "break"))
+ elif instr.operation == HighLevelILOperation.HLIL_CONTINUE:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "continue"))
+ elif instr.operation == HighLevelILOperation.HLIL_CALL:
+ self.perform_get_expr_text(instr.dest, tokens, settings, as_full_ast,
+ OperatorPrecedence.MemberAndFunctionOperatorPrecedence)
+ tokens.append_open_paren()
+ for (i, param) in enumerate(instr.params):
+ if i > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(param, tokens, settings)
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_TAILCALL:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "return "))
+ self.perform_get_expr_text(instr.dest, tokens, settings, as_full_ast,
+ OperatorPrecedence.MemberAndFunctionOperatorPrecedence)
+ tokens.append_open_paren()
+ for (i, param) in enumerate(instr.params):
+ if i > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(param, tokens, settings)
+ tokens.append_close_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, " # tailcall"))
+ elif instr.operation == HighLevelILOperation.HLIL_INTRINSIC:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, instr.intrinsic.name))
+ tokens.append_open_paren()
+ for (i, param) in enumerate(instr.params):
+ if i > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(param, tokens, settings)
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_SYSCALL:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "syscall"))
+ tokens.append_open_paren()
+ for (i, param) in enumerate(instr.params):
+ if i > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ if i == 0 and param.operation == HighLevelILOperation.HLIL_CONST:
+ platform = self.function.platform
+ if platform is not None:
+ syscall_name = platform.get_system_call_name(param.constant)
+ if len(syscall_name) > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, syscall_name))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " "))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "{"))
+ self.perform_get_expr_text(param, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "}"))
+ continue
+ self.perform_get_expr_text(param, tokens, settings)
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_ZX:
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ elif instr.operation == HighLevelILOperation.HLIL_SX:
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ elif instr.operation == HighLevelILOperation.HLIL_IMPORT:
+ # Check for import address symbol at target address, and display that if there is one
+ sym = instr.function.source_function.view.get_symbol_at(instr.constant)
+ if sym is not None:
+ if sym.type == SymbolType.ImportedDataSymbol or sym.type == SymbolType.ImportAddressSymbol:
+ sym = sym.imported_function_from_import_address_symbol(instr.constant)
+ if sym is not None:
+ tokens.append(
+ InstructionTextToken(InstructionTextTokenType.IndirectImportToken, sym.short_name,
+ value=instr.constant, address=instr.address,
+ size=instr.size, operand=instr.source_operand))
+ return
+ # Otherwise use a generic pointer token
+ tokens.append_pointer_text_token(instr, instr.constant, settings,
+ SymbolDisplayType.DereferenceNonDataSymbols, precedence)
+ elif instr.operation == HighLevelILOperation.HLIL_ARRAY_INDEX:
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.MemberAndFunctionOperatorPrecedence)
+ tokens.append_open_bracket()
+ self.perform_get_expr_text(instr.index, tokens, settings)
+ tokens.append_close_bracket()
+ elif instr.operation == HighLevelILOperation.HLIL_VAR_INIT:
+ # Check to see if the variable appears live
+ appears_dead = False
+ ssa = instr.ssa_form
+ if ssa is not None and ssa.operation == HighLevelILOperation.HLIL_VAR_INIT_SSA:
+ appears_dead = not self.hlil.is_ssa_var_live(ssa.dest)
+
+ # If the variable does not appear live, show the assignment as zero confidence (grayed out)
+ with tokens.force_zero_confidence(appears_dead):
+ tokens.append_var_text_token(instr.dest, instr, instr.size)
+ if instr.dest.type is not None:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": "))
+ tokens.append(instr.dest.type.get_tokens())
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " = "))
+
+ # For the right side of the assignment, only use zero confidence if the instruction does
+ # not have any side effects
+ with tokens.force_zero_confidence(appears_dead and not instr.src.has_side_effects):
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.AssignmentOperatorPrecedence)
+ elif instr.operation == HighLevelILOperation.HLIL_VAR_DECLARE:
+ tokens.append_var_text_token(instr.var, instr, instr.size)
+ if instr.var.type is not None:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": "))
+ tokens.append(instr.var.type.get_tokens())
+ elif instr.operation == HighLevelILOperation.HLIL_FLOAT_CONST:
+ # The constant value in the instruction contains the raw bits of the floating point value. Convert
+ # this to a floating point value and display it.
+ if instr.size == 4:
+ value = struct.unpack("<f", struct.pack("<I", instr.constant))[0]
+ tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}"))
+ elif instr.size == 8:
+ value = struct.unpack("<d", struct.pack("<Q", instr.constant))[0]
+ tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}"))
+ else:
+ tokens.append_integer_text_token(instr, instr.constant, instr.size)
+ elif instr.operation == HighLevelILOperation.HLIL_CONST:
+ # Check for bool type. Display these as True or False. The default handling will use C style
+ # booleans instead of Python style.
+ if instr.size == 0 or isinstance(instr.expr_type, BoolType):
+ if instr.constant != 0:
+ tokens.append(
+ InstructionTextToken(InstructionTextTokenType.IntegerToken, "True", address=instr.address,
+ value=instr.constant))
+ else:
+ tokens.append(
+ InstructionTextToken(InstructionTextTokenType.IntegerToken, "False", address=instr.address,
+ value=instr.constant))
+ else:
+ tokens.append_constant_text_token(instr, instr.constant, instr.size, settings, precedence)
+ elif instr.operation == HighLevelILOperation.HLIL_CONST_PTR:
+ tokens.append_pointer_text_token(instr, instr.constant, settings,
+ SymbolDisplayType.AddressOfDataSymbols, precedence)
+ elif instr.operation == HighLevelILOperation.HLIL_CONST_DATA:
+ # Constant data should be rendered according to the type of builtin function being used.
+ data, builtin = instr.constant_data.data_and_builtin
+ if builtin in [BuiltinType.BuiltinStrcpy, BuiltinType.BuiltinStrncpy]:
+ data = data.escape(null_terminates=True)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, f'"{data}"',
+ address=instr.address, value=instr.constant_data.value,
+ context=InstructionTextTokenContext.ConstStringDataTokenContext))
+ elif builtin == BuiltinType.BuiltinMemset:
+ tokens.append_open_brace()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken,
+ f"{instr.constant_data.value:#x}",
+ address=instr.address, value=instr.constant_data.value,
+ context=InstructionTextTokenContext.ConstDataTokenContext))
+ tokens.append_close_brace()
+ else:
+ string, string_type = self.function.view.stringify_unicode_data(self.function.arch, data)
+ if string is not None:
+ wide_string_prefix = ""
+ token_context = InstructionTextTokenContext.ConstDataTokenContext
+ if builtin == BuiltinType.BuiltinWcscpy:
+ wide_string_prefix = "L"
+ token_context = InstructionTextTokenContext.ConstStringDataTokenContext
+ tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken,
+ f'{wide_string_prefix}"{string}"',
+ address=instr.address, value=instr.constant_data.value,
+ context=token_context))
+ else:
+ data = data.escape(null_terminates=False, escape_printable=True)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, f'"{data}"',
+ address=instr.address, value=instr.constant_data.value,
+ context=InstructionTextTokenContext.ConstDataTokenContext))
+ elif instr.operation == HighLevelILOperation.HLIL_EXTERN_PTR:
+ # Extern pointer instructions have an offset associated with them. If this is nonzero, show this
+ # as an addition or subtraction of the offset.
+ parens = instr.offset != 0 and precedence >= OperatorPrecedence.AddOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ if instr.offset != 0:
+ precedence = OperatorPrecedence.SubOperatorPrecedence
+ tokens.append_pointer_text_token(instr, instr.constant, settings,
+ SymbolDisplayType.AddressOfDataSymbols, precedence)
+ if instr.offset < 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " - "))
+ tokens.append_integer_text_token(instr, -instr.offset, instr.size)
+ elif instr.offset > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " + "))
+ tokens.append_integer_text_token(instr, instr.offset, instr.size)
+
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_VAR:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.LocalVariableToken, instr.var.name,
+ address=instr.expr_index, size=instr.size,
+ value=instr.var.identifier,
+ context=InstructionTextTokenContext.LocalVariableTokenContext))
+ elif instr.operation == HighLevelILOperation.HLIL_ASSIGN:
+ # Check to see if the variable appears live
+ appears_dead = False
+ if instr.dest.operation == HighLevelILOperation.HLIL_VAR:
+ ssa = instr.ssa_form
+ if ssa is not None and ssa.operation == HighLevelILOperation.HLIL_VAR_INIT_SSA:
+ appears_dead = not self.hlil.is_ssa_var_live(ssa.dest)
+
+ # If the variable does not appear live, show the assignment as zero confidence (grayed out)
+ with tokens.force_zero_confidence(appears_dead):
+ self.perform_get_expr_text(instr.dest, tokens, settings, as_full_ast,
+ OperatorPrecedence.AssignmentOperatorPrecedence)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " = "))
+
+ # For the right side of the assignment, only use zero confidence if the instruction does
+ # not have any side effects
+ with tokens.force_zero_confidence(appears_dead and not instr.src.has_side_effects):
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.AssignmentOperatorPrecedence)
+ elif instr.operation == HighLevelILOperation.HLIL_ASSIGN_UNPACK:
+ tokens.append_open_paren()
+ for (i, dest) in enumerate(instr.dest):
+ if i > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(dest, tokens, settings)
+ tokens.append_close_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " = "))
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.AssignmentOperatorPrecedence)
+ elif instr.operation == HighLevelILOperation.HLIL_STRUCT_FIELD:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.MemberAndFunctionOperatorPrecedence)
+ self.append_field_text_tokens(instr.src, instr.offset, instr.member_index, instr.size, tokens)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_DEREF_FIELD:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ if instr.src.operation == HighLevelILOperation.HLIL_CONST_PTR:
+ tokens.append_pointer_text_token(instr.src, instr.src.constant, settings,
+ SymbolDisplayType.DisplaySymbolOnly,
+ OperatorPrecedence.MemberAndFunctionOperatorPrecedence)
+ else:
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.MemberAndFunctionOperatorPrecedence)
+ self.append_field_text_tokens(instr.src, instr.offset, instr.member_index, instr.size, tokens)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_DEREF:
+ if instr.src.operation == HighLevelILOperation.HLIL_CONST_PTR:
+ if tokens.append_pointer_text_token(instr.src, instr.src.constant, settings,
+ SymbolDisplayType.DereferenceNonDataSymbols,
+ precedence) == SymbolDisplayResult.DataSymbolResult:
+ expr_type = instr.src.expr_type
+ if isinstance(expr_type, PointerType) and expr_type.target.width != instr.size:
+ # If dereference size doesn't match the data variable size, append a size suffix
+ suffix = {0: "", 1: ".b", 2: ".w", 4: ".d", 8: ".q", 10: ".t", 16: ".q"}
+ if instr.size in suffix:
+ suffix_str = suffix[instr.size]
+ else:
+ suffix_str = f".{instr.size}"
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, suffix_str))
+ else:
+ parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "*"))
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.UnaryOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_ADDRESS_OF:
+ parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "&"))
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.UnaryOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_CMP_E, HighLevelILOperation.HLIL_FCMP_E]:
+ parens = precedence > OperatorPrecedence.EqualityOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast,
+ OperatorPrecedence.EqualityOperatorPrecedence)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " == "))
+ self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast,
+ OperatorPrecedence.EqualityOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_CMP_NE, HighLevelILOperation.HLIL_FCMP_NE]:
+ parens = precedence > OperatorPrecedence.EqualityOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast,
+ OperatorPrecedence.EqualityOperatorPrecedence)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " != "))
+ self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast,
+ OperatorPrecedence.EqualityOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_CMP_SLT, HighLevelILOperation.HLIL_CMP_ULT,
+ HighLevelILOperation.HLIL_FCMP_LT]:
+ parens = precedence > OperatorPrecedence.CompareOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast,
+ OperatorPrecedence.CompareOperatorPrecedence)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " < "))
+ self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast,
+ OperatorPrecedence.CompareOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_CMP_SLE, HighLevelILOperation.HLIL_CMP_ULE,
+ HighLevelILOperation.HLIL_FCMP_LE]:
+ parens = precedence > OperatorPrecedence.CompareOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast,
+ OperatorPrecedence.CompareOperatorPrecedence)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " <= "))
+ self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast,
+ OperatorPrecedence.CompareOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_CMP_SGE, HighLevelILOperation.HLIL_CMP_UGE,
+ HighLevelILOperation.HLIL_FCMP_GE]:
+ parens = precedence > OperatorPrecedence.CompareOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast,
+ OperatorPrecedence.CompareOperatorPrecedence)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " >= "))
+ self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast,
+ OperatorPrecedence.CompareOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_CMP_SGT, HighLevelILOperation.HLIL_CMP_UGT,
+ HighLevelILOperation.HLIL_FCMP_GT]:
+ parens = precedence > OperatorPrecedence.CompareOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast,
+ OperatorPrecedence.CompareOperatorPrecedence)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " > "))
+ self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast,
+ OperatorPrecedence.CompareOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_AND:
+ if instr.size == 0:
+ # Size of zero is a boolean operation, show the boolean operator name
+ parens = (precedence >= OperatorPrecedence.BitwiseOrOperatorPrecedence or
+ precedence == OperatorPrecedence.LogicalOrOperatorPrecedence)
+ operation = "and"
+ precedence = OperatorPrecedence.LogicalAndOperatorPrecedence
+ else:
+ parens = (precedence >= OperatorPrecedence.EqualityOperatorPrecedence or
+ precedence in [OperatorPrecedence.BitwiseOrOperatorPrecedence,
+ OperatorPrecedence.BitwiseXorOperatorPrecedence])
+ operation = "&"
+ precedence = OperatorPrecedence.BitwiseAndOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens(operation, instr, tokens, settings, as_full_ast, precedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_OR:
+ if instr.size == 0:
+ # Size of zero is a boolean operation, show the boolean operator name
+ parens = (precedence >= OperatorPrecedence.BitwiseOrOperatorPrecedence or
+ precedence == OperatorPrecedence.LogicalAndOperatorPrecedence)
+ operation = "or"
+ precedence = OperatorPrecedence.LogicalOrOperatorPrecedence
+ else:
+ parens = (precedence >= OperatorPrecedence.EqualityOperatorPrecedence or
+ precedence in [OperatorPrecedence.BitwiseAndOperatorPrecedence,
+ OperatorPrecedence.BitwiseXorOperatorPrecedence])
+ operation = "|"
+ precedence = OperatorPrecedence.BitwiseOrOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens(operation, instr, tokens, settings, as_full_ast, precedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_XOR:
+ parens = (precedence >= OperatorPrecedence.EqualityOperatorPrecedence or
+ precedence in [OperatorPrecedence.BitwiseAndOperatorPrecedence,
+ OperatorPrecedence.BitwiseOrOperatorPrecedence])
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens("^", instr, tokens, settings, as_full_ast,
+ OperatorPrecedence.BitwiseXorOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_ADC:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "adc"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.carry, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_SBB:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "sbb"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.carry, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_ADD_OVERFLOW:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "add_overflow"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_ADD, HighLevelILOperation.HLIL_FADD]:
+ parens = (precedence > OperatorPrecedence.AddOperatorPrecedence or
+ precedence in [OperatorPrecedence.ShiftOperatorPrecedence,
+ OperatorPrecedence.BitwiseAndOperatorPrecedence,
+ OperatorPrecedence.BitwiseOrOperatorPrecedence,
+ OperatorPrecedence.BitwiseXorOperatorPrecedence])
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens("+", instr, tokens, settings, as_full_ast,
+ OperatorPrecedence.AddOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_SUB, HighLevelILOperation.HLIL_FSUB]:
+ parens = (precedence > OperatorPrecedence.AddOperatorPrecedence or
+ precedence in [OperatorPrecedence.ShiftOperatorPrecedence,
+ OperatorPrecedence.BitwiseAndOperatorPrecedence,
+ OperatorPrecedence.BitwiseOrOperatorPrecedence,
+ OperatorPrecedence.BitwiseXorOperatorPrecedence])
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens("-", instr, tokens, settings, as_full_ast,
+ OperatorPrecedence.SubOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_MUL, HighLevelILOperation.HLIL_MULS_DP,
+ HighLevelILOperation.HLIL_MULU_DP, HighLevelILOperation.HLIL_FMUL]:
+ parens = (precedence > OperatorPrecedence.MultiplyOperatorPrecedence or
+ precedence in [OperatorPrecedence.ShiftOperatorPrecedence,
+ OperatorPrecedence.BitwiseAndOperatorPrecedence,
+ OperatorPrecedence.BitwiseOrOperatorPrecedence,
+ OperatorPrecedence.BitwiseXorOperatorPrecedence])
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens("*", instr, tokens, settings, as_full_ast,
+ OperatorPrecedence.MultiplyOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_DIVS, HighLevelILOperation.HLIL_DIVU,
+ HighLevelILOperation.HLIL_DIVS_DP, HighLevelILOperation.HLIL_DIVU_DP]:
+ parens = (precedence > OperatorPrecedence.DivideOperatorPrecedence or
+ precedence in [OperatorPrecedence.ShiftOperatorPrecedence,
+ OperatorPrecedence.BitwiseAndOperatorPrecedence,
+ OperatorPrecedence.BitwiseOrOperatorPrecedence,
+ OperatorPrecedence.BitwiseXorOperatorPrecedence])
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens("//", instr, tokens, settings, as_full_ast,
+ OperatorPrecedence.DivideOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_MODS, HighLevelILOperation.HLIL_MODU,
+ HighLevelILOperation.HLIL_MODS_DP, HighLevelILOperation.HLIL_MODU_DP]:
+ parens = (precedence > OperatorPrecedence.DivideOperatorPrecedence or
+ precedence in [OperatorPrecedence.ShiftOperatorPrecedence,
+ OperatorPrecedence.BitwiseAndOperatorPrecedence,
+ OperatorPrecedence.BitwiseOrOperatorPrecedence,
+ OperatorPrecedence.BitwiseXorOperatorPrecedence])
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens("%", instr, tokens, settings, as_full_ast,
+ OperatorPrecedence.DivideOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_FDIV:
+ parens = (precedence > OperatorPrecedence.DivideOperatorPrecedence or
+ precedence in [OperatorPrecedence.ShiftOperatorPrecedence,
+ OperatorPrecedence.BitwiseAndOperatorPrecedence,
+ OperatorPrecedence.BitwiseOrOperatorPrecedence,
+ OperatorPrecedence.BitwiseXorOperatorPrecedence])
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens("/", instr, tokens, settings, as_full_ast,
+ OperatorPrecedence.DivideOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_LSL:
+ parens = precedence > OperatorPrecedence.ShiftOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens("<<", instr, tokens, settings, as_full_ast,
+ OperatorPrecedence.ShiftOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_LSR, HighLevelILOperation.HLIL_ASR]:
+ parens = precedence > OperatorPrecedence.ShiftOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.append_two_operand_tokens(">>", instr, tokens, settings, as_full_ast,
+ OperatorPrecedence.ShiftOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_ROL:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "rol"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_ROR:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "ror"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_RLC:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "rlc"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.carry, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_RRC:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "rrc"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.carry, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_TEST_BIT:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "test_bit"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_FLOOR:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "floor"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_CEIL:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "ceil"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_FTRUNC:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "trunc"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_FABS:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "fabs"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_FSQRT:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "sqrt"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_ROUND_TO_INT:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "round"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_FCMP_O:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "fcmp_o"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_FCMP_UO:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "fcmp_uo"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.left, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.right, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_NOT:
+ parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ if instr.size == 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "not "))
+ else:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "~"))
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.UnaryOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_NEG, HighLevelILOperation.HLIL_FNEG]:
+ parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "-"))
+ self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast,
+ OperatorPrecedence.UnaryOperatorPrecedence)
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_FLOAT_CONV, HighLevelILOperation.HLIL_INT_TO_FLOAT]:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "float"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_FLOAT_TO_INT, HighLevelILOperation.HLIL_BOOL_TO_INT]:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "int"))
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ tokens.append_close_paren()
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_RET:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "return"))
+ operands = instr.src
+ if len(operands) > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " "))
+ if len(operands) > 1:
+ tokens.append_open_paren()
+ for (i, operand) in enumerate(operands):
+ if i > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(operand, tokens, settings)
+ if len(operands) > 1:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_NORET:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# no return"))
+ elif instr.operation == HighLevelILOperation.HLIL_UNREACHABLE:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# unreachable"))
+ elif instr.operation == HighLevelILOperation.HLIL_UNDEF:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# undefined"))
+ elif instr.operation == HighLevelILOperation.HLIL_NOP:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# nop"))
+ elif instr.operation == HighLevelILOperation.HLIL_BP:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "breakpoint"))
+ tokens.append_open_paren()
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_JUMP:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# jump -> "))
+ self.perform_get_expr_text(instr.dest, tokens, settings)
+ elif instr.operation == HighLevelILOperation.HLIL_TRAP:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "trap"))
+ tokens.append_open_paren()
+ tokens.append_integer_text_token(instr, instr.vector, 8)
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_GOTO:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "goto "))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.GotoLabelToken, instr.target.name,
+ instr.target.label_id))
+ elif instr.operation == HighLevelILOperation.HLIL_LABEL:
+ tokens.decrease_indent()
+ tokens.append(InstructionTextToken(InstructionTextTokenType.GotoLabelToken, instr.target.name,
+ instr.target.label_id))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":"))
+ tokens.increase_indent()
+ elif instr.operation == HighLevelILOperation.HLIL_LOW_PART:
+ parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence
+ if parens:
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.src, tokens, settings)
+ suffix = {0: "", 1: ".b", 2: ".w", 4: ".d", 8: ".q", 10: ".t", 16: ".q"}
+ if instr.size in suffix:
+ suffix_str = suffix[instr.size]
+ else:
+ suffix_str = f".{instr.size}"
+ tokens.append(InstructionTextToken(InstructionTextTokenType.StructOffsetToken, suffix_str, value=0,
+ size=instr.size))
+ if parens:
+ tokens.append_close_paren()
+ elif instr.operation == HighLevelILOperation.HLIL_SPLIT:
+ tokens.append_open_paren()
+ self.perform_get_expr_text(instr.high, tokens, settings)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ self.perform_get_expr_text(instr.low, tokens, settings)
+ tokens.append_close_paren()
+ elif instr.operation in [HighLevelILOperation.HLIL_UNIMPL, HighLevelILOperation.HLIL_UNIMPL_MEM]:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# "))
+ for token in instr.tokens:
+ token.token_type = InstructionTextTokenType.AnnotationToken
+ tokens.append(token)
+ else:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken,
+ f"# unimplemented {instr.operation.name}"))
+
+ def append_two_operand_tokens(self, operation: str, instr: HighLevelILInstruction, tokens: HighLevelILTokenEmitter,
+ settings: Optional[DisassemblySettings], as_full_ast: bool,
+ precedence: OperatorPrecedence):
+ if precedence == OperatorPrecedence.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
+ left_precedence = OperatorPrecedence.AddOperatorPrecedence
+ elif precedence == OperatorPrecedence.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
+ left_precedence = OperatorPrecedence.MultiplyOperatorPrecedence
+ else:
+ left_precedence = precedence
+
+ self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, left_precedence)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, f" {operation} "))
+ self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, precedence)
+
+ def append_field_text_tokens(self, var: HighLevelILInstruction, offset: int, member_index: int, size: int,
+ tokens: HighLevelILTokenEmitter):
+ var_type = var.expr_type
+ # Follow pointer type to its target
+ if isinstance(var_type, PointerType):
+ var_type = var_type.target
+ # Follow named type references to the target
+ if isinstance(var_type, NamedTypeReferenceType):
+ target_type = var_type.target(var.function.view)
+ if target_type is not None:
+ var_type = target_type
+
+ has_field = False
+ if isinstance(var_type, StructureType):
+ # For structures, resolve field names using the type API
+ class Resolver:
+ def __init__(self, view: BinaryView, offset: int):
+ self.has_field = False
+ self.correct_size = False
+ self.offset = offset
+ self.view = view
+
+ def resolve_func(self, base_name: Optional[NamedTypeReferenceType],
+ resolved_struct: Optional[StructureType], resolved_member_index: int,
+ struct_offset: int, adjusted_offset: int, member: StructureMember):
+ tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "."))
+ name_list = HighLevelILTokenEmitter.names_for_outer_structure_members(
+ self.view, var_type, var) + [member.name]
+ tokens.append(InstructionTextToken(InstructionTextTokenType.FieldNameToken, member.name,
+ value=struct_offset + member.offset, typeNames=name_list))
+ self.offset = adjusted_offset - member.offset
+ self.has_field = True
+ self.correct_size = member.type is not None and size == member.type.width
+
+ resolver = Resolver(self.function.view, offset)
+ result = var_type.resolve_member_or_base_member(resolver.view, offset, 0, resolver.resolve_func)
+ if result and resolver.has_field and resolver.correct_size:
+ # If the field was matched, we're done
+ return
+ has_field = resolver.has_field
+ offset = resolver.offset
+
+ # Generate offset syntax for the missing field
+ suffix = {0: "", 1: ".b", 2: ".w", 4: ".d", 8: ".q", 10: ".t", 16: ".q"}
+ if size in suffix:
+ suffix_str = suffix[size]
+ else:
+ suffix_str = f".{size}"
+ if (has_field or not isinstance(var_type, StructureType)) and offset == 0:
+ # No offset, just display a size suffix
+ offset_str = suffix_str
+ else:
+ # Has an offset
+ offset_str = f".__offset({offset:#x}){suffix_str}"
+
+ name_list = HighLevelILTokenEmitter.names_for_outer_structure_members(
+ self.function.view, var_type, var) + [offset_str]
+ tokens.append(InstructionTextToken(InstructionTextTokenType.StructOffsetToken, offset_str, value=offset,
+ size=size, typeNames=name_list))
+
+
+class PseudoPythonFunctionType(LanguageRepresentationFunctionType):
+ language_name = "Pseudo Python"
+
+ def create(self, arch: Architecture, owner: Function, hlil: HighLevelILFunction):
+ return PseudoPythonFunction(arch, owner, hlil)
+
+ def function_type_tokens(self, func: Function, settings: DisassemblySettings) -> DisassemblyTextLine:
+ tokens = []
+ tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "def "))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.CodeSymbolToken, func.name, value=func.start))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, "("))
+ for (i, param) in enumerate(func.type.parameters_with_all_locations):
+ if i > 0:
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", "))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.ArgumentNameToken, param.name,
+ context=InstructionTextTokenContext.LocalVariableTokenContext,
+ address=param.location.identifier))
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": "))
+ for token in param.type.get_tokens():
+ token.context = InstructionTextTokenContext.LocalVariableTokenContext
+ token.address = param.location.identifier
+ tokens.append(token)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, ")"))
+ if func.can_return.value and func.type.return_value is not None and not isinstance(func.type.return_value, VoidType):
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " -> "))
+ for token in func.type.return_value.get_tokens():
+ token.context = InstructionTextTokenContext.FunctionReturnTokenContext
+ tokens.append(token)
+ tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":"))
+ return [DisassemblyTextLine(tokens, func.start)]
+
+
+PseudoPythonFunctionType().register()
diff --git a/python/function.py b/python/function.py
index 5ed61603..c8acf287 100644
--- a/python/function.py
+++ b/python/function.py
@@ -27,8 +27,9 @@ from dataclasses import dataclass
# Binary Ninja components
from . import _binaryninjacore as core
from .enums import (
- AnalysisSkipReason, FunctionGraphType, SymbolType, InstructionTextTokenType, HighlightStandardColor,
- HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType
+ AnalysisSkipReason, FunctionGraphType, SymbolType, InstructionTextTokenType, HighlightStandardColor,
+ HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType,
+ BuiltinType
)
from .exceptions import ILException
@@ -45,6 +46,7 @@ from . import variable
from . import flowgraph
from . import callingconvention
from . import workflow
+from . import languagerepresentation
from . import deprecation
from . import __version__
@@ -76,6 +78,7 @@ ILFunctionType = Union['lowlevelil.LowLevelILFunction', 'mediumlevelil.MediumLev
ILInstructionType = Union['lowlevelil.LowLevelILInstruction', 'mediumlevelil.MediumLevelILInstruction',
'highlevelil.HighLevelILInstruction']
StringOrType = Union[str, 'types.Type', 'types.TypeBuilder']
+FunctionViewTypeOrName = Union['FunctionViewType', FunctionGraphType, str]
def _function_name_():
@@ -181,6 +184,39 @@ class VariableReferenceSource:
return f"<var: {repr(self.var)}, src: {repr(self.src)}>"
+@dataclass
+class FunctionViewType:
+ view_type: FunctionGraphType
+ name: Optional[str]
+
+ def __init__(self, view_type: FunctionViewTypeOrName):
+ if isinstance(view_type, FunctionViewType):
+ self.view_type = view_type.view_type
+ self.name = view_type.name
+ if isinstance(view_type, FunctionGraphType):
+ self.view_type = view_type
+ self.name = None
+ else:
+ self.view_type = FunctionGraphType.HighLevelLanguageRepresentationFunctionGraph
+ self.name = str(view_type)
+
+ @staticmethod
+ def _from_core_struct(view_type: core.BNFunctionViewType) -> 'FunctionViewType':
+ if view_type.type == FunctionGraphType.HighLevelLanguageRepresentationFunctionGraph:
+ if view_type.name is None:
+ return FunctionViewType("Pseudo C")
+ else:
+ return FunctionViewType(view_type.name)
+ else:
+ return FunctionViewType(view_type.type)
+
+ def _to_core_struct(self) -> core.BNFunctionViewType:
+ result = core.BNFunctionViewType()
+ result.type = self.view_type
+ result.name = self.name
+ return result
+
+
class BasicBlockList:
def __init__(
self, function: Union['Function', 'lowlevelil.LowLevelILFunction', 'mediumlevelil.MediumLevelILFunction',
@@ -1069,6 +1105,30 @@ class Function:
return highlevelil.HighLevelILFunction(self.arch, result, self)
@property
+ def pseudo_c(self) -> Optional['languagerepresentation.LanguageRepresentationFunction']:
+ return self.language_representation("Pseudo C")
+
+ @property
+ def pseudo_c_if_available(self) -> Optional['languagerepresentation.LanguageRepresentationFunction']:
+ return self.language_representation_if_available("Pseudo C")
+
+ def language_representation(
+ self, language: str
+ ) -> Optional['languagerepresentation.LanguageRepresentationFunction']:
+ result = core.BNGetFunctionLanguageRepresentation(self.handle, language)
+ if result is None:
+ return None
+ return languagerepresentation.LanguageRepresentationFunction(handle=result)
+
+ def language_representation_if_available(
+ self, language: str
+ ) -> Optional['languagerepresentation.LanguageRepresentationFunction']:
+ result = core.BNGetFunctionLanguageRepresentationIfAvailable(self.handle, language)
+ if result is None:
+ return None
+ return languagerepresentation.LanguageRepresentationFunction(handle=result)
+
+ @property
def type(self) -> 'types.FunctionType':
"""
Function type object, can be set with either a string representing the function prototype
@@ -1787,7 +1847,15 @@ class Function:
core.BNFreeILInstructionList(exits)
def get_constant_data(self, state: RegisterValueType, value: int, size: int = 0) -> databuffer.DataBuffer:
- return databuffer.DataBuffer(handle=core.BNGetConstantData(self.handle, state, value, size))
+ return databuffer.DataBuffer(handle=core.BNGetConstantData(self.handle, state, value, size, None))
+
+ def get_constant_data_and_builtin(
+ self, state: RegisterValueType, value: int, size: int = 0
+ ) -> Tuple[databuffer.DataBuffer, BuiltinType]:
+ builtin = ctypes.c_int()
+ db = databuffer.DataBuffer(
+ handle=core.BNGetConstantData(self.handle, state, value, size, ctypes.byref(builtin)))
+ return db, BuiltinType(builtin.value)
def get_reg_value_at(
self, addr: int, reg: 'architecture.RegisterType', arch: Optional['architecture.Architecture'] = None
@@ -2078,13 +2146,14 @@ class Function:
return result
def create_graph(
- self, graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph,
+ self, graph_type: FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph,
settings: Optional['DisassemblySettings'] = None
) -> flowgraph.CoreFlowGraph:
if settings is not None:
settings_obj = settings.handle
else:
settings_obj = None
+ graph_type = FunctionViewType(graph_type)._to_core_struct()
return flowgraph.CoreFlowGraph(core.BNCreateFunctionGraph(self.handle, graph_type, settings_obj))
def apply_imported_types(self, sym: 'types.CoreSymbol', type: Optional[StringOrType] = None) -> None:
diff --git a/python/highlevelil.py b/python/highlevelil.py
index 48cbd6cc..21359685 100644
--- a/python/highlevelil.py
+++ b/python/highlevelil.py
@@ -907,6 +907,10 @@ class HighLevelILInstruction(BaseILInstruction):
return False
return True
+ @property
+ def has_side_effects(self) -> bool:
+ return core.BNHighLevelILHasSideEffects(self.function.handle, self.expr_index)
+
@dataclass(frozen=True, repr=False, eq=False)
class HighLevelILUnaryBase(HighLevelILInstruction, UnaryOperation):
diff --git a/python/languagerepresentation.py b/python/languagerepresentation.py
new file mode 100644
index 00000000..49a300c1
--- /dev/null
+++ b/python/languagerepresentation.py
@@ -0,0 +1,822 @@
+# Copyright (c) 2024 Vector 35 Inc
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import ctypes
+import traceback
+from typing import List, Optional, Union
+
+# Binary Ninja components
+import binaryninja
+from . import _binaryninjacore as core
+from . import architecture
+from . import binaryview
+from . import function
+from . import highlevelil
+from . import highlight
+from . import variable
+from . import types
+from .log import log_error
+from .enums import BraceRequirement, HighlightStandardColor, InstructionTextTokenType, OperatorPrecedence, ScopeType, \
+ SymbolDisplayType, SymbolDisplayResult
+
+
+class HighLevelILTokenEmitter:
+ """
+ ``class HighLevelILTokenEmitter`` contains methods for emitting text tokens for High Level IL instructions.
+ Methods are provided for typical patterns found in various high level languages.
+
+ This class cannot be instantiated directly. An instance of the class will be provided when the methods
+ in ``class LanguageRepresentationFunction`` are called.
+ """
+ def __init__(self, handle: core.BNHighLevelILTokenEmitterHandle):
+ self.handle = handle
+
+ def __del__(self):
+ if core is not None:
+ core.BNFreeHighLevelILTokenEmitter(self.handle)
+
+ def new_line(self):
+ """Starts a new line in the output."""
+ core.BNHighLevelILTokenEmitterNewLine(self.handle)
+
+ def increase_indent(self):
+ """Increases the indentation level by one."""
+ core.BNHighLevelILTokenEmitterIncreaseIndent(self.handle)
+
+ def decrease_indent(self):
+ """Decreases the indentation level by one."""
+ core.BNHighLevelILTokenEmitterDecreaseIndent(self.handle)
+
+ def scope_separator(self):
+ """
+ Indicates that visual separation of scopes is desirable at the current position. By default,
+ this will insert a blank line, but this can be configured by the user.
+ """
+ core.BNHighLevelILTokenEmitterScopeSeparator(self.handle)
+
+ def begin_scope(self, scope_type: ScopeType):
+ """Begins a new scope. Insertion of newlines and braces will be handled using the current settings."""
+ core.BNHighLevelILTokenEmitterBeginScope(self.handle, scope_type)
+
+ def end_scope(self, scope_type: ScopeType):
+ """Ends the current scope."""
+ core.BNHighLevelILTokenEmitterEndScope(self.handle, scope_type)
+
+ def scope_continuation(self, force_same_line: bool):
+ """Continues the previous scope with a new associated scope. This is most commonly used for ``else`` statements."""
+ core.BNHighLevelILTokenEmitterScopeContinuation(self.handle, force_same_line)
+
+ def finalize_scope(self):
+ """Finalizes the previous scope, indicating that there are no more associated scopes."""
+ core.BNHighLevelILTokenEmitterFinalizeScope(self.handle)
+
+ def no_indent_for_this_line(self):
+ """Forces there to be no indentation for the next line."""
+ core.BNHighLevelILTokenEmitterNoIndentForThisLine(self.handle)
+
+ class ZeroConfidenceContext:
+ """
+ ``class ZeroConfidenceContext`` is a context manager that optionally forces tokens to be of zero confidence
+ inside the context.
+ """
+ def __init__(self, emitter: 'HighLevelILTokenEmitter', enabled: bool):
+ self.emitter = emitter
+ self.enabled = enabled
+
+ def __enter__(self):
+ if self.enabled:
+ core.BNHighLevelILTokenEmitterBeginForceZeroConfidence(self.emitter.handle)
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ if self.enabled:
+ core.BNHighLevelILTokenEmitterEndForceZeroConfidence(self.emitter.handle)
+
+ def force_zero_confidence(self, enabled: bool = True) -> 'HighLevelILTokenEmitter.ZeroConfidenceContext':
+ """
+ Returns a context manager that forces tokens inside of it to be of zero confidence. If ``False`` is passed
+ to this method, the context has no effect, allowing the caller to conditionally apply this behavior.
+ """
+ return HighLevelILTokenEmitter.ZeroConfidenceContext(self, enabled)
+
+ class ExprContext:
+ """
+ ``class ExprContext`` is a context manager that associates the tokens inside the context with the given
+ High Level IL expression.
+ """
+ def __init__(self, emitter: 'HighLevelILTokenEmitter', hlil_expr: highlevelil.HighLevelILInstruction):
+ self.emitter = emitter
+ self.expr = hlil_expr
+ self.prev_expr = None
+
+ def __enter__(self):
+ expr = core.BNTokenEmitterExpr()
+ expr.address = self.expr.address
+ expr.sourceOperand = self.expr.source_operand
+ expr.exprIndex = self.expr.expr_index
+ expr.instrIndex = self.expr.instr_index
+ self.prev_expr = core.BNHighLevelILTokenEmitterSetCurrentExpr(self.emitter.handle, expr)
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ if self.prev_expr is not None:
+ core.BNHighLevelILTokenEmitterRestoreCurrentExpr(self.emitter.handle, self.prev_expr)
+
+ def expr(self, hlil_expr: 'highlevelil.HighLevelILInstruction') -> 'HighLevelILTokenEmitter.ExprContext':
+ """
+ Returns a context manager that associates the tokens inside the context with the given High Level IL expression.
+ """
+ return HighLevelILTokenEmitter.ExprContext(self, hlil_expr)
+
+ def finalize(self):
+ """Finalizes all tokens in the output."""
+ core.BNHighLevelILTokenEmitterFinalize(self.handle)
+
+ def append(self, tokens: Union['architecture.InstructionTextToken', List['architecture.InstructionTextToken']]):
+ """Appends a token or list of tokens to the output."""
+ if not isinstance(tokens, list):
+ tokens = [tokens]
+ buf = architecture.InstructionTextToken._get_core_struct(tokens)
+ for i in range(len(tokens)):
+ core.BNHighLevelILTokenEmitterAppend(self.handle, buf[i])
+
+ def append_open_paren(self):
+ """Appends an open parenthesis (``(``) to the output."""
+ core.BNHighLevelILTokenEmitterAppendOpenParen(self.handle)
+
+ def append_close_paren(self):
+ """Appends a close parenthesis (``)``) to the output."""
+ core.BNHighLevelILTokenEmitterAppendCloseParen(self.handle)
+
+ def append_open_bracket(self):
+ """Appends an open bracket (``[``) to the output."""
+ core.BNHighLevelILTokenEmitterAppendOpenBracket(self.handle)
+
+ def append_close_bracket(self):
+ """Appends a close bracket (``]``) to the output."""
+ core.BNHighLevelILTokenEmitterAppendCloseBracket(self.handle)
+
+ def append_open_brace(self):
+ """Appends an open brace (``{``) to the output."""
+ core.BNHighLevelILTokenEmitterAppendOpenBrace(self.handle)
+
+ def append_close_brace(self):
+ """Appends a close brace (``}``) to the output."""
+ core.BNHighLevelILTokenEmitterAppendCloseBrace(self.handle)
+
+ def append_semicolon(self):
+ """Appends a semicolon (``;``) to the output."""
+ core.BNHighLevelILTokenEmitterAppendSemicolon(self.handle)
+
+ @property
+ def current_tokens(self) -> List['function.InstructionTextToken']:
+ """The list of tokens on the current line (read-only)."""
+ count = ctypes.c_ulonglong()
+ tokens = core.BNHighLevelILTokenEmitterGetCurrentTokens(self.handle, count)
+ result = []
+ if tokens is not None:
+ result = function.InstructionTextToken._from_core_struct(tokens, count.value)
+ core.BNFreeInstructionText(tokens, count.value)
+ return result
+
+ @property
+ def lines(self) -> List['function.DisassemblyTextLine']:
+ """The list of lines in the output (read-only)."""
+ count = ctypes.c_ulonglong()
+ lines = core.BNHighLevelILTokenEmitterGetLines(self.handle, count)
+ result = []
+ if lines is not None:
+ result = []
+ for i in range(0, count.value):
+ addr = lines[i].addr
+ color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
+ tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count)
+ result.append(function.DisassemblyTextLine(tokens, addr, color=color))
+ core.BNFreeDisassemblyTextLines(lines, count.value)
+ return result
+
+ @property
+ def brace_requirement(self) -> BraceRequirement:
+ """The requirement for insertion of braces around scopes in the output."""
+ return core.BNHighLevelILTokenEmitterGetBraceRequirement(self.handle)
+
+ @brace_requirement.setter
+ def brace_requirement(self, value: BraceRequirement):
+ core.BNHighLevelILTokenEmitterSetBraceRequirement(self.handle, value)
+
+ @property
+ def braces_around_switch_cases(self):
+ """Whether cases within switch statements should always have braces around them."""
+ return core.BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(self.handle)
+
+ @braces_around_switch_cases.setter
+ def braces_around_switch_cases(self, value: bool):
+ core.BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(self.handle, value)
+
+ @property
+ def default_braces_on_same_line(self):
+ """
+ Whether braces should default to being on the same line as the statement that begins the scope.
+ If the user has explicitly set a preference, this setting will be ignored and the user's preference
+ will be used instead.
+ """
+ return core.BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(self.handle)
+
+ @default_braces_on_same_line.setter
+ def default_braces_on_same_line(self, value: bool):
+ core.BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(self.handle, value)
+
+ @property
+ def simple_scope_allowed(self):
+ """Whether omitting braces around single-line scopes is allowed."""
+ return core.BNHighLevelILTokenEmitterIsSimpleScopeAllowed(self.handle)
+
+ @simple_scope_allowed.setter
+ def simple_scope_allowed(self, value: bool):
+ core.BNHighLevelILTokenEmitterSetSimpleScopeAllowed(self.handle, value)
+
+ def append_size_token(self, size: int, token_type: InstructionTextTokenType):
+ """Appends a size token for the given size in the High Level IL syntax."""
+ core.BNAddHighLevelILSizeToken(size, token_type, self.handle)
+
+ def append_float_size_token(self, size: int, token_type: InstructionTextTokenType):
+ """Appends a floating point size token for the given size in the High Level IL syntax."""
+ core.BNAddHighLevelILFloatSizeToken(size, token_type, self.handle)
+
+ def append_var_text_token(
+ self, var: 'variable.CoreVariable', instr: 'highlevelil.HighLevelILInstruction',
+ size: int
+ ):
+ """Appends tokens for access to a variable."""
+ core.BNAddHighLevelILVarTextToken(
+ instr.function.handle, var.to_BNVariable(), self.handle, instr.expr_index, size)
+
+ def append_integer_text_token(self, instr: 'highlevelil.HighLevelILInstruction', value: int, size: int):
+ """Appends tokens for a constant intenger value."""
+ core.BNAddHighLevelILIntegerTextToken(instr.function.handle, instr.expr_index, value, size, self.handle)
+
+ def append_array_index_token(
+ self, instr: 'highlevelil.HighLevelILInstruction', value: int, size: int, address: int = 0):
+ """Appends tokens for accessing an array by index."""
+ core.BNAddHighLevelILArrayIndexToken(instr.function.handle, instr.expr_index, value, size, self.handle, address)
+
+ def append_pointer_text_token(
+ self, instr: 'highlevelil.HighLevelILInstruction', value: int,
+ settings: Optional['function.DisassemblySettings'], symbol_display: SymbolDisplayType,
+ precedence: OperatorPrecedence, allow_short_string: bool = False
+ ) -> SymbolDisplayResult:
+ """Appends tokens for displaying a constant pointer value."""
+ if settings is not None:
+ settings = settings.handle
+ return SymbolDisplayResult(core.BNAddHighLevelILPointerTextToken(
+ instr.function.handle, instr.expr_index, value, self.handle, settings, symbol_display, precedence,
+ allow_short_string))
+
+ def append_constant_text_token(
+ self, instr: 'highlevelil.HighLevelILInstruction', value: int, size: int,
+ settings: Optional['function.DisassemblySettings'], precedence: OperatorPrecedence
+ ):
+ """Appends tokens for a constant value."""
+ if settings is not None:
+ settings = settings.handle
+ core.BNAddHighLevelILConstantTextToken(
+ instr.function.handle, instr.expr_index, value, size, self.handle, settings, precedence)
+
+ @staticmethod
+ def names_for_outer_structure_members(
+ view: 'binaryview.BinaryView', struct_type: 'types.Type', var: 'highlevelil.HighLevelILInstruction',
+ ) -> List[str]:
+ """
+ Gets the list of names for the outer structure members when accessing a structure member. This list
+ can be passed for the ``typeNames`` parameter of the ``class InstructionTextToken`` constructor.
+ """
+ count = ctypes.c_ulonglong()
+ result = core.BNAddNamesForOuterStructureMembers(
+ view.handle, struct_type.handle, var.function.handle, var.expr_index, count)
+ names = []
+ if result is not None:
+ for i in range(count.value):
+ names.append(result[i].decode("utf-8"))
+ core.BNFreeStringList(result, count.value)
+ return names
+
+
+class LanguageRepresentationFunction:
+ """
+ ``class LanguageRepresentationFunction`` represents a single function in a registered high level language.
+ """
+ _registered_instances = []
+ comment_start_string = "// "
+ comment_end_string = ""
+ annotation_start_string = "{"
+ annotation_end_string = "}"
+
+ def __init__(
+ self, arch: Optional['architecture.Architecture'] = None, owner: Optional['function.Function'] = None,
+ hlil: Optional['highlevelil.HighLevelILFunction'] = None, handle=None
+ ):
+ if handle is None:
+ if arch is None:
+ raise ValueError("function representation must have an associated architecture")
+ if owner is None:
+ raise ValueError("function representation must have an owning function")
+ if hlil is None:
+ raise ValueError("function representation must have an associated High Level IL function")
+ self._cb = core.BNCustomLanguageRepresentationFunction()
+ self._cb.context = 0
+ self._cb.externalRefTaken = self._cb.externalRefTaken.__class__(self._external_ref_taken)
+ self._cb.externalRefReleased = self._cb.externalRefReleased.__class__(self._external_ref_released)
+ self._cb.initTokenEmitter = self._cb.initTokenEmitter.__class__(self._init_token_emitter)
+ self._cb.getExprText = self._cb.getExprText.__class__(self._get_expr_text)
+ self._cb.beginLines = self._cb.beginLines.__class__(self._begin_lines)
+ self._cb.endLines = self._cb.endLines.__class__(self._end_lines)
+ self._cb.getCommentStartString = self._cb.getCommentStartString.__class__(self._comment_start_string)
+ self._cb.getCommentEndString = self._cb.getCommentEndString.__class__(self._comment_end_string)
+ self._cb.getAnnotationStartString = self._cb.getAnnotationStartString.__class__(
+ self._annotation_start_string)
+ self._cb.getAnnotationEndString = self._cb.getAnnotationEndString.__class__(self._annotation_end_string)
+ self.comment_start_string = self.__class__.comment_start_string
+ self.comment_end_string = self.__class__.comment_end_string
+ self.annotation_start_string = self.__class__.annotation_start_string
+ self.annotation_end_string = self.__class__.annotation_end_string
+ _handle = core.BNCreateCustomLanguageRepresentationFunction(arch.handle, owner.handle, hlil.handle, self._cb)
+ assert _handle is not None
+ else:
+ self.comment_start_string = core.BNGetLanguageRepresentationFunctionCommentStartString(handle)
+ self.comment_end_string = core.BNGetLanguageRepresentationFunctionCommentEndString(handle)
+ self.annotation_start_string = core.BNGetLanguageRepresentationFunctionAnnotationStartString(handle)
+ self.annotation_end_string = core.BNGetLanguageRepresentationFunctionAnnotationEndString(handle)
+ _handle = handle
+ assert _handle is not None
+ self.handle: core.BNLanguageRepresentationFunctionHandle = _handle
+
+ def __del__(self):
+ if core is not None:
+ core.BNFreeLanguageRepresentationFunction(self.handle)
+
+ def _external_ref_taken(self, ctxt):
+ try:
+ self.__class__._registered_instances.append(self)
+ except:
+ log_error(traceback.format_exc())
+
+ def _external_ref_released(self, ctxt):
+ try:
+ self.__class__._registered_instances.remove(self)
+ except:
+ log_error(traceback.format_exc())
+
+ def _init_token_emitter(self, ctxt, emitter: core.BNHighLevelILTokenEmitterHandle):
+ try:
+ emitter = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(emitter))
+ self.perform_init_token_emitter(emitter)
+ except:
+ log_error(traceback.format_exc())
+
+ def _get_expr_text(
+ self, ctxt, hlil: core.BNHighLevelILFunctionHandle, expr_index: int,
+ tokens: core.BNHighLevelILTokenEmitterHandle, settings: Optional[core.BNDisassemblySettingsHandle],
+ as_full_ast: bool, precedence: OperatorPrecedence, statement: bool
+ ):
+ try:
+ hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil))
+ instr = hlil.get_expr(highlevelil.ExpressionIndex(expr_index))
+ tokens = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens))
+ if settings is not None:
+ settings = function.DisassemblySettings(core.BNNewDisassemblySettingsReference(settings))
+ self.perform_get_expr_text(instr, tokens, settings, as_full_ast, precedence, statement)
+ except:
+ log_error(traceback.format_exc())
+
+ def _begin_lines(
+ self, ctxt, hlil: core.BNHighLevelILFunctionHandle, expr_index: int,
+ tokens: core.BNHighLevelILTokenEmitterHandle
+ ):
+ try:
+ hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil))
+ instr = hlil.get_expr(highlevelil.ExpressionIndex(expr_index))
+ tokens = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens))
+ self.perform_begin_lines(instr, tokens)
+ except:
+ log_error(traceback.format_exc())
+
+ def _end_lines(
+ self, ctxt, hlil: core.BNHighLevelILFunctionHandle, expr_index: int,
+ tokens: core.BNHighLevelILTokenEmitterHandle
+ ):
+ try:
+ hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil))
+ instr = hlil.get_expr(highlevelil.ExpressionIndex(expr_index))
+ tokens = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens))
+ self.perform_end_lines(instr, tokens)
+ except:
+ log_error(traceback.format_exc())
+
+ def _comment_start_string(self, ctxt):
+ try:
+ return core.BNAllocString(self.comment_start_string)
+ except:
+ log_error(traceback.format_exc())
+ return core.BNAllocString("// ")
+
+ def _comment_end_string(self, ctxt):
+ try:
+ return core.BNAllocString(self.comment_end_string)
+ except:
+ log_error(traceback.format_exc())
+ return core.BNAllocString("")
+
+ def _annotation_start_string(self, ctxt):
+ try:
+ return core.BNAllocString(self.annotation_start_string)
+ except:
+ log_error(traceback.format_exc())
+ return core.BNAllocString("{")
+
+ def _annotation_end_string(self, ctxt):
+ try:
+ return core.BNAllocString(self.annotation_end_string)
+ except:
+ log_error(traceback.format_exc())
+ return core.BNAllocString("}")
+
+ def perform_init_token_emitter(self, emitter: HighLevelILTokenEmitter):
+ """Override this method to initialize the options for the token emitter before it is used."""
+ pass
+
+ def perform_get_expr_text(
+ self, instr: 'highlevelil.HighLevelILInstruction', tokens: HighLevelILTokenEmitter,
+ settings: Optional['function.DisassemblySettings'], as_full_ast: bool = True,
+ precedence: OperatorPrecedence = OperatorPrecedence.TopLevelOperatorPrecedence, statement: bool = False
+ ):
+ """
+ This method must be overridden by all language representation plugins.
+
+ This method is called to emit the tokens for a given High Level IL instruction.
+ """
+ raise NotImplementedError
+
+ def perform_begin_lines(self, instr: highlevelil.HighLevelILInstruction, tokens: HighLevelILTokenEmitter):
+ """This method can be overridden to emit tokens at the start of a function."""
+ pass
+
+ def perform_end_lines(self, instr: highlevelil.HighLevelILInstruction, tokens: HighLevelILTokenEmitter):
+ """This method can be overridden to emit tokens at the end of a function."""
+ pass
+
+ def get_expr_text(
+ self, instr: 'highlevelil.HighLevelILInstruction', settings: Optional['function.DisassemblySettings'],
+ as_full_ast: bool = True, precedence: OperatorPrecedence = OperatorPrecedence.TopLevelOperatorPrecedence,
+ statement: bool = False
+ ) -> List['function.DisassemblyTextLine']:
+ """Gets the lines of tokens for a given High Level IL instruction."""
+ count = ctypes.c_ulonglong()
+ if settings is not None:
+ settings = settings.handle
+ lines = core.BNGetLanguageRepresentationFunctionExprText(self.handle, instr.function.handle, instr.expr_index,
+ settings, as_full_ast, precedence, statement, count)
+ result = []
+ if lines is not None:
+ result = []
+ for i in range(0, count.value):
+ addr = lines[i].addr
+ if lines[i].instrIndex != 0xffffffffffffffff:
+ il_instr = instr.function[lines[i].instrIndex] # type: ignore
+ else:
+ il_instr = None
+ color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
+ tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count)
+ result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color))
+ core.BNFreeDisassemblyTextLines(lines, count.value)
+ return result
+
+ def get_linear_lines(
+ self, instr: 'highlevelil.HighLevelILInstruction',
+ settings: Optional['function.DisassemblySettings'] = None,
+ as_full_ast: bool = True
+ ) -> List['function.DisassemblyTextLine']:
+ """
+ Generates lines for the given High Level IL instruction in the style of the linear view. To get the lines
+ for the entire function, pass the ``root`` property of a ``class HighLevelILFunction``.
+ """
+ count = ctypes.c_ulonglong()
+ if settings is not None:
+ settings = settings.handle
+ lines = core.BNGetLanguageRepresentationFunctionLinearLines(self.handle, instr.function.handle, instr.expr_index,
+ settings, as_full_ast, count)
+ result = []
+ if lines is not None:
+ result = []
+ for i in range(0, count.value):
+ addr = lines[i].addr
+ if lines[i].instrIndex != 0xffffffffffffffff:
+ il_instr = instr.function[lines[i].instrIndex] # type: ignore
+ else:
+ il_instr = None
+ color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
+ tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count)
+ result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color))
+ core.BNFreeDisassemblyTextLines(lines, count.value)
+ return result
+
+ def get_block_lines(
+ self, block: 'highlevelil.HighLevelILBasicBlock', settings: Optional['function.DisassemblySettings'] = None
+ ) -> List['function.DisassemblyTextLine']:
+ """Generates lines for a single High Level IL basic block."""
+ count = ctypes.c_ulonglong()
+ if settings is not None:
+ settings = settings.handle
+ lines = core.BNGetLanguageRepresentationFunctionBlockLines(self.handle, block.handle, settings, count)
+ result = []
+ if lines is not None:
+ result = []
+ for i in range(0, count.value):
+ addr = lines[i].addr
+ if lines[i].instrIndex != 0xffffffffffffffff:
+ il_instr = instr.function[lines[i].instrIndex] # type: ignore
+ else:
+ il_instr = None
+ color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
+ tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count)
+ result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color))
+ core.BNFreeDisassemblyTextLines(lines, count.value)
+ return result
+
+ @property
+ def arch(self) -> 'architecture.Architecture':
+ return architecture.CoreArchitecture._from_cache(core.BNGetLanguageRepresentationArchitecture(self.handle))
+
+ @property
+ def function(self) -> 'function.Function':
+ return function.Function(handle=core.BNGetLanguageRepresentationOwnerFunction(self.handle))
+
+ @property
+ def high_level_il(self) -> 'highlevelil.HighLevelILFunction':
+ return self.hlil
+
+ @property
+ def hlil(self) -> 'highlevelil.HighLevelILFunction':
+ return highlevelil.HighLevelILFunction(arch=self.arch,
+ handle=core.BNGetLanguageRepresentationILFunction(self.handle), source_func=self.function)
+
+
+class _LanguageRepresentationFunctionTypeMetaClass(type):
+ def __iter__(self):
+ binaryninja._init_plugins()
+ count = ctypes.c_ulonglong()
+ types = core.BNGetLanguageRepresentationFunctionTypeList(count)
+ assert types is not None, "core.BNGetLanguageRepresentationFunctionTypeList returned None"
+ try:
+ for i in range(0, count.value):
+ yield CoreLanguageRepresentationFunctionType(handle=types[i])
+ finally:
+ core.BNFreeLanguageRepresentationFunctionTypeList(types)
+
+ def __getitem__(cls, value):
+ binaryninja._init_plugins()
+ lang = core.BNGetLanguageRepresentationFunctionTypeByName(str(value))
+ if lang is None:
+ raise KeyError("'%s' is not a valid language" % str(value))
+ return CoreLanguageRepresentationFunctionType(handle=lang)
+
+
+class LanguageRepresentationFunctionType(metaclass=_LanguageRepresentationFunctionTypeMetaClass):
+ """
+ ``class LanguageRepresentationFunctionType`` represents a custom language representation function type.
+ This class provides methods to create ``class LanguageRepresentationFunction`` instances for functions, as well
+ as manage the printing and parsing of types.
+ """
+ _registered_languages = []
+ language_name = None
+
+ def __init__(self, handle=None):
+ if handle is not None:
+ self.handle = core.handle_of_type(handle, core.BNLanguageRepresentationFunctionType)
+
+ def register(self):
+ """Registers the language representation function type."""
+ if self.__class__.language_name is None:
+ raise ValueError("Language name is missing")
+ self._cb = core.BNCustomLanguageRepresentationFunctionType()
+ self._cb.context = 0
+ self._cb.create = self._cb.create.__class__(self._create)
+ self._cb.isValid = self._cb.isValid.__class__(self._is_valid)
+ self._cb.getTypePrinter = self._cb.getTypePrinter.__class__(self._type_printer)
+ self._cb.getTypeParser = self._cb.getTypeParser.__class__(self._type_parser)
+ self._cb.getFunctionTypeTokens = self._cb.getFunctionTypeTokens.__class__(self._function_type_tokens)
+ self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines)
+ self.handle = core.BNRegisterLanguageRepresentationFunctionType(self.__class__.language_name, self._cb)
+ self.__class__._registered_languages.append(self)
+
+ def _create(
+ self, ctxt, arch: core.BNArchitectureHandle, owner: core.BNFunctionHandle,
+ hlil: core.BNHighLevelILFunctionHandle
+ ):
+ try:
+ arch = architecture.CoreArchitecture._from_cache(arch)
+ owner = function.Function(handle=core.BNNewFunctionReference(owner))
+ hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil))
+ result = self.create(arch, owner, hlil)
+ if result is None:
+ raise ValueError(f"create returned None for language representation '{self.name}'")
+ handle = core.BNNewLanguageRepresentationFunctionReference(result.handle)
+ assert handle is not None, "core.BNNewLanguageRepresentationFunctionReference returned None"
+ return ctypes.cast(handle, ctypes.c_void_p).value
+ except:
+ log_error(traceback.format_exc())
+ return None
+
+ def _is_valid(self, ctxt, view: core.BNBinaryViewHandle) -> bool:
+ try:
+ view = binaryview.BinaryView(handle=core.BNNewViewReference(view))
+ return self.is_valid(view)
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _type_printer(self, ctxt):
+ try:
+ result = self.type_printer
+ if result is None:
+ return None
+ return ctypes.cast(result.handle, ctypes.c_void_p).value
+ except:
+ log_error(traceback.format_exc())
+ return None
+
+ def _type_parser(self, ctxt):
+ try:
+ result = self.type_parser
+ if result is None:
+ return None
+ return ctypes.cast(result.handle, ctypes.c_void_p).value
+ except:
+ log_error(traceback.format_exc())
+ return None
+
+ def _function_type_tokens(
+ self, ctxt, func: core.BNFunctionHandle, settings: Optional[core.BNDisassemblySettingsHandle],
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ):
+ try:
+ func = function.Function(handle=core.BNNewFunctionReference(func))
+ if settings is not None:
+ settings = function.DisassemblySettings(handle=core.BNNewDisassemblySettingsReference(settings))
+ lines = self.function_type_tokens(func, settings)
+
+ count[0] = len(lines)
+ self.line_buf = (core.BNDisassemblyTextLine * len(lines))()
+ for i in range(len(lines)):
+ line = lines[i]
+ color = line.highlight
+ if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor):
+ raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor")
+ if isinstance(color, HighlightStandardColor):
+ color = highlight.HighlightColor(color)
+ self.line_buf[i].highlight = color._to_core_struct()
+ if line.address is None:
+ if len(line.tokens) > 0:
+ self.line_buf[i].addr = line.tokens[0].address
+ else:
+ self.line_buf[i].addr = 0
+ else:
+ self.line_buf[i].addr = line.address
+ if line.il_instruction is not None:
+ self.line_buf[i].instrIndex = line.il_instruction.instr_index
+ else:
+ self.line_buf[i].instrIndex = 0xffffffffffffffff
+
+ self.line_buf[i].count = len(line.tokens)
+ self.line_buf[i].tokens = function.InstructionTextToken._get_core_struct(line.tokens)
+
+ return ctypes.cast(self.line_buf, ctypes.c_void_p).value
+ except:
+ log_error(traceback.format_exc())
+ count[0] = 0
+ return None
+
+ def _free_lines(self, ctxt, lines, count):
+ self.line_buf = None
+
+ @property
+ def name(self) -> str:
+ if hasattr(self, 'handle'):
+ return core.BNGetLanguageRepresentationFunctionTypeName(self.handle)
+ return self.__class__.language_name
+
+ def create(
+ self, arch: 'architecture.Architecture', owner: 'function.Function', hlil: 'highlevelil.HighLevelILFunction'
+ ) -> LanguageRepresentationFunction:
+ """
+ This method must be overridden. This creates the ``class LanguageRepresentationFunction`` object for the
+ given architecture, owner function, and High Level IL function.
+ """
+ raise NotImplementedError
+
+ def is_valid(self, view: 'binaryview.BinaryView') -> bool:
+ """Returns whether the language is valid for the given binary view."""
+ return True
+
+ @property
+ def type_printer(self) -> Optional['binaryninja.typeprinter.TypePrinter']:
+ """
+ Returns the type printer for displaying types in this language. If ``None`` is returned, the default type
+ printer will be used.
+ """
+ return None
+
+ @property
+ def type_parser(self) -> Optional['binaryninja.typeparser.TypeParser']:
+ """
+ Returns the type parser for parsing types in this language. If ``None`` is returned, the default type
+ parser will be used.
+ """
+ return None
+
+ def function_type_tokens(
+ self, func: 'function.Function', settings: Optional['function.DisassemblySettings']
+ ) -> List['function.DisassemblyTextLine']:
+ """
+ Returns a list of lines representing a function prototype in this language. If no lines are returned, the
+ default C-style prototype will be used.
+ """
+ return []
+
+ def __repr__(self):
+ return f"<LanguageRepresentationFunctionType: {self.name}>"
+
+
+_language_cache = {}
+
+
+class CoreLanguageRepresentationFunctionType(LanguageRepresentationFunctionType):
+ def __init__(self, handle: core.BNLanguageRepresentationFunctionTypeHandle):
+ super(CoreLanguageRepresentationFunctionType, self).__init__(handle=handle)
+ if type(self) is CoreLanguageRepresentationFunctionType:
+ global _language_cache
+ _language_cache[ctypes.addressof(handle.contents)] = self
+
+ @classmethod
+ def _from_cache(cls, handle) -> 'LanguageRepresentationFunctionType':
+ """
+ Look up a representation type from a given BNLanguageRepresentationFunctionType handle
+ :param handle: BNLanguageRepresentationFunctionType pointer
+ :return: Respresentation type instance responsible for this handle
+ """
+ global _language_cache
+ return _language_cache.get(ctypes.addressof(handle.contents)) or cls(handle)
+
+ def create(
+ self, arch: 'architecture.Architecture', owner: 'function.Function', hlil: 'highlevelil.HighLevelILFunction'
+ ) -> LanguageRepresentationFunction:
+ raise NotImplementedError
+
+ def is_valid(self, view: 'binaryview.BinaryView') -> bool:
+ return core.BNIsLanguageRepresentationFunctionTypeValid(self.handle, view.handle)
+
+ @property
+ def type_printer(self) -> Optional['binaryninja.typeprinter.TypePrinter']:
+ result = core.BNGetLanguageRepresentationFunctionTypePrinter(self.handle)
+ if result is None:
+ return None
+ return binaryninja.typeprinter.TypePrinter(handle=result)
+
+ @property
+ def type_parser(self) -> Optional['binaryninja.typeparser.TypeParser']:
+ result = core.BNGetLanguageRepresentationFunctionTypeParser(self.handle)
+ if result is None:
+ return None
+ return binaryninja.typeparser.TypeParser(handle=result)
+
+ def function_type_tokens(
+ self, func: 'function.Function', settings: Optional['function.DisassemblySettings']
+ ) -> List['function.DisassemblyTextLine']:
+ count = ctypes.c_ulonglong()
+ if settings is not None:
+ settings = settings.handle
+ lines = core.BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens(self.handle, func.handle, settings, count)
+ result = []
+ if lines is not None:
+ result = []
+ for i in range(0, count.value):
+ addr = lines[i].addr
+ color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
+ tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count)
+ result.append(function.DisassemblyTextLine(tokens, addr, color=color))
+ core.BNFreeDisassemblyTextLines(lines, count.value)
+ return result
diff --git a/python/lineardisassembly.py b/python/lineardisassembly.py
index a19148ca..40a0d6c5 100644
--- a/python/lineardisassembly.py
+++ b/python/lineardisassembly.py
@@ -345,12 +345,13 @@ class LinearViewObject:
@staticmethod
def language_representation(
- view: 'binaryview.BinaryView', settings: Optional['_function.DisassemblySettings'] = None
+ view: 'binaryview.BinaryView', settings: Optional['_function.DisassemblySettings'] = None,
+ language: str = "Pseudo C"
) -> 'LinearViewObject':
_settings = settings
if _settings is not None:
_settings = _settings.handle
- return LinearViewObject(core.BNCreateLinearViewLanguageRepresentation(view.handle, _settings))
+ return LinearViewObject(core.BNCreateLinearViewLanguageRepresentation(view.handle, _settings, language))
@staticmethod
def data_only(
@@ -453,12 +454,13 @@ class LinearViewObject:
@staticmethod
def single_function_language_representation(
- func: '_function.Function', settings: Optional['_function.DisassemblySettings'] = None
+ func: '_function.Function', settings: Optional['_function.DisassemblySettings'] = None,
+ language: str = "Pseudo C"
) -> 'LinearViewObject':
_settings = settings
if _settings is not None:
_settings = _settings.handle
- return LinearViewObject(core.BNCreateLinearViewSingleFunctionLanguageRepresentation(func.handle, _settings))
+ return LinearViewObject(core.BNCreateLinearViewSingleFunctionLanguageRepresentation(func.handle, _settings, language))
class LinearViewCursor:
diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py
index 02b070fa..4b76939f 100644
--- a/python/scriptingprovider.py
+++ b/python/scriptingprovider.py
@@ -1809,23 +1809,23 @@ def _get_current_il_function(instance: PythonScriptingInstance):
from binaryninja import FunctionGraphType
if instance.interpreter.locals["current_ui_view_location"] is not None:
ilType = instance.interpreter.locals["current_ui_view_location"].getILViewType()
- if ilType == FunctionGraphType.LowLevelILFunctionGraph:
+ if ilType.view_type == FunctionGraphType.LowLevelILFunctionGraph:
return instance.interpreter.locals["current_llil"]
- elif ilType == FunctionGraphType.LiftedILFunctionGraph:
+ elif ilType.view_type == FunctionGraphType.LiftedILFunctionGraph:
return instance.interpreter.locals["current_lifted_il"]
- elif ilType == FunctionGraphType.LowLevelILSSAFormFunctionGraph:
+ elif ilType.view_type == FunctionGraphType.LowLevelILSSAFormFunctionGraph:
return instance.interpreter.locals["current_llil_ssa"]
- elif ilType == FunctionGraphType.MappedMediumLevelILFunctionGraph:
+ elif ilType.view_type == FunctionGraphType.MappedMediumLevelILFunctionGraph:
return instance.interpreter.locals["current_mapped_mlil"]
- elif ilType == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph:
+ elif ilType.view_type == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph:
return instance.interpreter.locals["current_mapped_mlil_ssa"]
- elif ilType == FunctionGraphType.MediumLevelILFunctionGraph:
+ elif ilType.view_type == FunctionGraphType.MediumLevelILFunctionGraph:
return instance.interpreter.locals["current_mlil"]
- elif ilType == FunctionGraphType.MediumLevelILSSAFormFunctionGraph:
+ elif ilType.view_type == FunctionGraphType.MediumLevelILSSAFormFunctionGraph:
return instance.interpreter.locals["current_mlil_ssa"]
- elif ilType == FunctionGraphType.HighLevelILFunctionGraph:
+ elif ilType.view_type == FunctionGraphType.HighLevelILFunctionGraph:
return instance.interpreter.locals["current_hlil"]
- elif ilType == FunctionGraphType.HighLevelILSSAFormFunctionGraph:
+ elif ilType.view_type == FunctionGraphType.HighLevelILSSAFormFunctionGraph:
return instance.interpreter.locals["current_hlil_ssa"]
return None
diff --git a/python/types.py b/python/types.py
index 2c2b652f..d5fd3544 100644
--- a/python/types.py
+++ b/python/types.py
@@ -20,7 +20,7 @@
import ctypes
import typing
-from typing import Generator, List, Union, Tuple, Optional, Iterable, Dict, Generic, TypeVar
+from typing import Generator, List, Union, Tuple, Optional, Iterable, Dict, Generic, TypeVar, Callable
from dataclasses import dataclass
import uuid
@@ -53,6 +53,7 @@ SomeType = Union['TypeBuilder', 'Type']
TypeContainerType = Union['binaryview.BinaryView', 'typelibrary.TypeLibrary']
NameSpaceType = Optional[Union[str, List[str], 'NameSpace']]
TypeParserResult = typeparser.TypeParserResult
+ResolveMemberCallback = Callable[['NamedTypeReferenceType', 'StructureType', int, int, int, 'StructureMember'], None]
# The following are needed to prevent the type checker from getting
# confused as we have member functions in `Type` named the same thing
_int = int
@@ -384,6 +385,12 @@ class CoreSymbol:
def handle(self):
return self._handle
+ def imported_function_from_import_address_symbol(self, addr: int) -> Optional['CoreSymbol']:
+ sym = core.BNImportedFunctionFromImportAddressSymbol(self._handle, addr)
+ if sym is None:
+ return None
+ return CoreSymbol(sym)
+
class Symbol(CoreSymbol):
"""
@@ -2717,6 +2724,40 @@ class StructureType(Type):
ntr_type, guid, name, self.alignment, self.width, self.platform, self.confidence
)
+ def resolve_member_or_base_member(
+ self, view: Optional['binaryview.BinaryView'], offset: int, size: int,
+ resolve_func: ResolveMemberCallback, member_index_hint: Optional[int] = None
+ ) -> bool:
+ if view is not None:
+ view = view.handle
+
+ def resolve_member_callback(
+ ctxt, base_name: core.BNNamedTypeReferenceHandle, resolved_struct: core.BNStructureHandle,
+ member_index: int, struct_offset: int, adjusted_offset: int, member: core.BNStructureMember
+ ):
+ if base_name:
+ base_name = NamedTypeReferenceType.create_from_handle(core.BNNewNamedTypeReference(base_name))
+ if resolved_struct:
+ resolved_struct = StructureType.from_core_struct(core.BNNewStructureReference(resolved_struct))
+ t = Type.create(core.BNNewTypeReference(member.type), confidence=member.typeConfidence)
+ struct_member = StructureMember(
+ t, member.name, member.offset, MemberAccess(member.access), MemberScope(member.scope)
+ )
+ resolve_func(base_name, resolved_struct, member_index, struct_offset, adjusted_offset, struct_member)
+
+ member_index_hint_value = 0
+ if member_index_hint is not None:
+ member_index_hint_value = member_index_hint
+ return core.BNResolveStructureMemberOrBaseMember(
+ self.struct_handle, view, offset, size, None,
+ ctypes.CFUNCTYPE(
+ None, ctypes.c_void_p, core.BNNamedTypeReferenceHandle, core.BNStructureHandle, ctypes.c_size_t,
+ ctypes.c_uint64, ctypes.c_uint64, core.BNStructureMember
+ )(resolve_member_callback),
+ member_index_hint is not None,
+ member_index_hint_value
+ )
+
@property
def children(self) -> List[Type]:
return [member.type for member in self.members]
@@ -3064,6 +3105,30 @@ class FunctionType(Type):
return result
@property
+ def parameters_with_all_locations(self) -> List[FunctionParameter]:
+ """Type parameters list with default locations filled in with values (read-only)"""
+ count = ctypes.c_ulonglong()
+ params = core.BNGetTypeParameters(self._handle, count)
+ assert params is not None, "core.BNGetTypeParameters returned None"
+ result = []
+ for i in range(0, count.value):
+ param_type = Type.create(
+ core.BNNewTypeReference(params[i].type), platform=self._platform, confidence=params[i].typeConfidence
+ )
+ name = params[i].name
+ if (params[i].location.type
+ == VariableSourceType.RegisterVariableSourceType) and (self._platform is not None):
+ name = self._platform.arch.get_reg_name(params[i].location.storage)
+ elif params[i].location.type == VariableSourceType.StackVariableSourceType:
+ name = "arg_%x" % params[i].location.storage
+ param_location = variable.VariableNameAndType(
+ params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type
+ )
+ result.append(FunctionParameter(param_type, params[i].name, param_location))
+ core.BNFreeTypeParameterList(params, count.value)
+ return result
+
+ @property
def has_variable_arguments(self) -> BoolWithConfidence:
"""Whether type has variable arguments (read-only)"""
result = core.BNTypeHasVariableArguments(self._handle)
diff --git a/python/variable.py b/python/variable.py
index 8ed3f545..7b4121bf 100644
--- a/python/variable.py
+++ b/python/variable.py
@@ -20,14 +20,14 @@
# IN THE SOFTWARE.
import ctypes
-from typing import List, Generator, Optional, Union, Set, Dict
+from typing import List, Generator, Optional, Union, Set, Dict, Tuple
from dataclasses import dataclass
import binaryninja
from . import _binaryninjacore as core
from . import databuffer
from . import decorators
-from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination, FunctionGraphType
+from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination, FunctionGraphType, BuiltinType
FunctionOrILFunction = Union["binaryninja.function.Function", "binaryninja.lowlevelil.LowLevelILFunction",
"binaryninja.mediumlevelil.MediumLevelILFunction",
@@ -223,6 +223,12 @@ class ConstantData(RegisterValue):
raise ValueError(f"ConstantData requires a Function instance: {self.size}")
return self.function.get_constant_data(self.type, self.value, self.size)
+ @property
+ def data_and_builtin(self) -> Tuple[databuffer.DataBuffer, BuiltinType]:
+ if self.function is None:
+ raise ValueError(f"ConstantData requires a Function instance: {self.size}")
+ return self.function.get_constant_data_and_builtin(self.type, self.value, self.size)
+
@dataclass(frozen=True)
class ValueRange:
diff --git a/rust/examples/decompile/src/main.rs b/rust/examples/decompile/src/main.rs
index cac5ae06..f4f2a0ff 100644
--- a/rust/examples/decompile/src/main.rs
+++ b/rust/examples/decompile/src/main.rs
@@ -18,7 +18,7 @@ fn decompile_to_c(view: &BinaryView, func: &Function) {
settings.set_option(DisassemblyOption::ShowAddress, false);
settings.set_option(DisassemblyOption::WaitForIL, true);
- let linearview = LinearViewObject::language_representation(view, &settings);
+ let linearview = LinearViewObject::language_representation(view, &settings, "Pseudo C");
let mut cursor = LinearViewCursor::new(&linearview);
cursor.seek_to_address(func.highest_address());
diff --git a/rust/src/databuffer.rs b/rust/src/databuffer.rs
index c3d268cd..29d7636b 100644
--- a/rust/src/databuffer.rs
+++ b/rust/src/databuffer.rs
@@ -109,8 +109,8 @@ impl DataBuffer {
}
}
- pub fn to_escaped_string(&self, null_terminates: bool) -> BnString {
- unsafe { BnString::from_raw(BNDataBufferToEscapedString(self.0, null_terminates)) }
+ pub fn to_escaped_string(&self, null_terminates: bool, escape_printable: bool) -> BnString {
+ unsafe { BnString::from_raw(BNDataBufferToEscapedString(self.0, null_terminates, escape_printable)) }
}
pub fn from_escaped_string(value: &BnString) -> Self {
diff --git a/rust/src/function.rs b/rust/src/function.rs
index 01b086f2..0508ca4d 100644
--- a/rust/src/function.rs
+++ b/rust/src/function.rs
@@ -39,6 +39,7 @@ use crate::{databuffer::DataBuffer, disassembly::InstructionTextToken, rc::*};
pub use binaryninjacore_sys::BNAnalysisSkipReason as AnalysisSkipReason;
pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisSkipOverride;
pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType;
+pub use binaryninjacore_sys::BNBuiltinType as BuiltinType;
use std::{fmt, mem};
use std::{ffi::c_char, hash::Hash, ops::Range};
@@ -124,6 +125,78 @@ impl BlockContext for NativeBlock {
}
}
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum FunctionViewType {
+ Normal,
+ LowLevelIL,
+ LiftedIL,
+ LowLevelILSSAForm,
+ MediumLevelIL,
+ MediumLevelILSSAForm,
+ MappedMediumLevelIL,
+ MappedMediumLevelILSSAForm,
+ HighLevelIL,
+ HighLevelILSSAForm,
+ HighLevelLanguageRepresentation(String),
+}
+
+pub(crate) struct RawFunctionViewType(pub BNFunctionViewType);
+
+impl FunctionViewType {
+ pub(crate) fn as_raw(&self) -> RawFunctionViewType {
+ let view_type = match self {
+ FunctionViewType::Normal => BNFunctionGraphType::NormalFunctionGraph,
+ FunctionViewType::LowLevelIL => BNFunctionGraphType::LowLevelILFunctionGraph,
+ FunctionViewType::LiftedIL => BNFunctionGraphType::LiftedILFunctionGraph,
+ FunctionViewType::LowLevelILSSAForm => BNFunctionGraphType::LowLevelILSSAFormFunctionGraph,
+ FunctionViewType::MediumLevelIL => BNFunctionGraphType::MediumLevelILFunctionGraph,
+ FunctionViewType::MediumLevelILSSAForm => BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph,
+ FunctionViewType::MappedMediumLevelIL => BNFunctionGraphType::MappedMediumLevelILFunctionGraph,
+ FunctionViewType::MappedMediumLevelILSSAForm => BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph,
+ FunctionViewType::HighLevelIL => BNFunctionGraphType::HighLevelILFunctionGraph,
+ FunctionViewType::HighLevelILSSAForm => BNFunctionGraphType::HighLevelILSSAFormFunctionGraph,
+ FunctionViewType::HighLevelLanguageRepresentation(_) => BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph,
+ };
+ RawFunctionViewType(BNFunctionViewType {
+ type_: view_type,
+ name: if let FunctionViewType::HighLevelLanguageRepresentation(ref name) = self {
+ std::ffi::CString::new(name.to_string()).unwrap().into_raw()
+ } else {
+ std::ptr::null()
+ },
+ })
+ }
+}
+
+impl Into<FunctionViewType> for FunctionGraphType {
+ fn into(self) -> FunctionViewType {
+ match self {
+ BNFunctionGraphType::LowLevelILFunctionGraph => FunctionViewType::LowLevelIL,
+ BNFunctionGraphType::LiftedILFunctionGraph => FunctionViewType::LiftedIL,
+ BNFunctionGraphType::LowLevelILSSAFormFunctionGraph => FunctionViewType::LowLevelILSSAForm,
+ BNFunctionGraphType::MediumLevelILFunctionGraph => FunctionViewType::MediumLevelIL,
+ BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph => FunctionViewType::MediumLevelILSSAForm,
+ BNFunctionGraphType::MappedMediumLevelILFunctionGraph => FunctionViewType::MappedMediumLevelIL,
+ BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph => FunctionViewType::MappedMediumLevelILSSAForm,
+ BNFunctionGraphType::HighLevelILFunctionGraph => FunctionViewType::HighLevelIL,
+ BNFunctionGraphType::HighLevelILSSAFormFunctionGraph => FunctionViewType::HighLevelILSSAForm,
+ BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph => {
+ FunctionViewType::HighLevelLanguageRepresentation("Pseudo C".into()
+ )
+ }
+ _ => FunctionViewType::Normal,
+ }
+ }
+}
+
+impl Drop for RawFunctionViewType {
+ fn drop(&mut self) {
+ if !self.0.name.is_null() {
+ unsafe { let _ = std::ffi::CString::from_raw(self.0.name as *mut _); }
+ }
+ }
+}
+
#[derive(Eq)]
pub struct Function {
pub(crate) handle: *mut BNFunction,
@@ -1205,10 +1278,12 @@ impl Function {
state: RegisterValueType,
value: u64,
size: Option<usize>,
- ) -> DataBuffer {
+ ) -> (DataBuffer, BuiltinType) {
let size = size.unwrap_or(0);
let state_raw = state.into_raw_value();
- DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state_raw, value, size) })
+ let mut builtin_type = BuiltinType::BuiltinNone;
+ let buffer = DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state_raw, value, size, &mut builtin_type) });
+ (buffer, builtin_type)
}
pub fn constants_referenced_by(
@@ -2141,11 +2216,11 @@ impl Function {
pub fn create_graph(
&self,
- graph_type: FunctionGraphType,
+ view_type: FunctionViewType,
settings: Option<DisassemblySettings>,
) -> Ref<FlowGraph> {
let settings_raw = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut());
- let result = unsafe { BNCreateFunctionGraph(self.handle, graph_type, settings_raw) };
+ let result = unsafe { BNCreateFunctionGraph(self.handle, view_type.as_raw().0, settings_raw) };
unsafe { Ref::new(FlowGraph::from_raw(result)) }
}
diff --git a/rust/src/linearview.rs b/rust/src/linearview.rs
index 84b4323e..b90d2784 100644
--- a/rust/src/linearview.rs
+++ b/rust/src/linearview.rs
@@ -103,11 +103,17 @@ impl LinearViewObject {
}
}
- pub fn language_representation(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
+ pub fn language_representation(
+ view: &BinaryView,
+ settings: &DisassemblySettings,
+ language: &str,
+ ) -> Ref<Self> {
unsafe {
+ let language = std::ffi::CString::new(language).unwrap();
let handle = binaryninjacore_sys::BNCreateLinearViewLanguageRepresentation(
view.handle,
settings.handle,
+ language.as_ptr(),
);
Self::from_raw(handle)
@@ -195,12 +201,15 @@ impl LinearViewObject {
pub fn single_function_language_representation(
function: &Function,
settings: &DisassemblySettings,
+ language: &str,
) -> Ref<Self> {
unsafe {
+ let language = std::ffi::CString::new(language).unwrap();
let handle =
binaryninjacore_sys::BNCreateLinearViewSingleFunctionLanguageRepresentation(
function.handle,
settings.handle,
+ language.as_ptr(),
);
Self::from_raw(handle)
diff --git a/type.cpp b/type.cpp
index 45e20463..94319367 100644
--- a/type.cpp
+++ b/type.cpp
@@ -1116,6 +1116,19 @@ BNIntegerDisplayType Type::GetIntegerTypeDisplayType() const
return BNGetIntegerTypeDisplayType(m_object);
}
+
+BNNameType Type::GetNameType() const
+{
+ return BNTypeGetNameType(m_object);
+}
+
+
+bool Type::ShouldDisplayReturnType() const
+{
+ return BNTypeShouldDisplayReturnType(m_object);
+}
+
+
string Type::GenerateAutoTypeId(const string& source, const QualifiedName& name)
{
BNQualifiedName nameObj = name.GetAPIObject();
@@ -1215,6 +1228,15 @@ Ref<NamedTypeReference> Type::GetRegisteredName() const
}
+string Type::GetAlternateName() const
+{
+ char* name = BNGetTypeAlternateName(m_object);
+ string result = name;
+ BNFreeString(name);
+ return result;
+}
+
+
Ref<Type> Type::WithReplacedStructure(Structure* from, Structure* to)
{
BNType* result = BNTypeWithReplacedStructure(m_object, from->GetObject(), to->GetObject());
@@ -2544,6 +2566,40 @@ Ref<Structure> Structure::WithReplacedNamedTypeReference(NamedTypeReference* fro
}
+struct ResolveMemberCallbackInfo
+{
+ const std::function<void(NamedTypeReference*, Structure*, size_t,
+ uint64_t, uint64_t, const StructureMember&)>* callback;
+};
+
+
+static void ResolveMemberCallback(void* ctxt, BNNamedTypeReference* baseName, BNStructure* resolvedStruct,
+ size_t memberIndex, uint64_t structOffset, uint64_t adjustedOffset, BNStructureMember member)
+{
+ ResolveMemberCallbackInfo* resolveFunc = (ResolveMemberCallbackInfo*)ctxt;
+ Ref<NamedTypeReference> baseNameRef = baseName ? new NamedTypeReference(BNNewNamedTypeReference(baseName)) : nullptr;
+ Ref<Structure> resolvedStructRef = resolvedStruct ? new Structure(BNNewStructureReference(resolvedStruct)) : nullptr;
+ StructureMember apiMember;
+ apiMember.type = Confidence<Ref<Type>>(new Type(BNNewTypeReference(member.type)), member.typeConfidence);
+ apiMember.name = member.name;
+ apiMember.offset = member.offset;
+ apiMember.access = member.access;
+ apiMember.scope = member.scope;
+ (*resolveFunc->callback)(baseNameRef, resolvedStructRef, memberIndex, structOffset, adjustedOffset, apiMember);
+}
+
+
+bool Structure::ResolveMemberOrBaseMember(BinaryView* data, uint64_t offset, size_t size,
+ const std::function<void(NamedTypeReference* baseName, Structure* s, size_t memberIndex,
+ uint64_t structOffset, uint64_t adjustedOffset, const StructureMember& member)>& resolveFunc,
+ std::optional<size_t> memberIndexHint)
+{
+ ResolveMemberCallbackInfo callbackInfo = { &resolveFunc };
+ return BNResolveStructureMemberOrBaseMember(m_object, data ? data->GetObject() : nullptr, offset, size,
+ &callbackInfo, ResolveMemberCallback, memberIndexHint.has_value(), memberIndexHint.value_or(0));
+}
+
+
StructureBuilder::StructureBuilder()
{
m_object = BNCreateStructureBuilder();
diff --git a/ui/commands.h b/ui/commands.h
index 70ed5778..37ed068a 100644
--- a/ui/commands.h
+++ b/ui/commands.h
@@ -27,11 +27,11 @@ bool BINARYNINJAUIAPI askForNewType(QWidget* parent, std::optional<BinaryNinja::
bool BINARYNINJAUIAPI inputNewType(QWidget* parent, BinaryViewRef data, FunctionRef currentFunction,
uint64_t currentAddr, size_t selectionSize, HighlightTokenState& highlight);
bool BINARYNINJAUIAPI createInferredMember(QWidget* parent, BinaryViewRef data, HighlightTokenState& highlight,
- FunctionRef func, BNFunctionGraphType ilType, size_t instrIndex);
+ FunctionRef func, const BinaryNinja::FunctionViewType& ilType, size_t instrIndex);
bool BINARYNINJAUIAPI createStructMembers(
QWidget* parent, BinaryViewRef data, HighlightTokenState& highlight, FunctionRef func);
-bool BINARYNINJAUIAPI inputPossibleValueSet(QWidget* parent, BinaryViewRef data, FunctionRef currentFunction, BNFunctionGraphType funcType,
+bool BINARYNINJAUIAPI inputPossibleValueSet(QWidget* parent, BinaryViewRef data, FunctionRef currentFunction, const BinaryNinja::FunctionViewType& funcType,
const BinaryNinja::Variable& var, size_t ilInstructionIndex = BN_INVALID_EXPR);
bool BINARYNINJAUIAPI getEnumSelection(QWidget* parent, BinaryViewRef data, FunctionRef func, uint64_t constValue,
@@ -55,12 +55,12 @@ uint64_t BINARYNINJAUIAPI getInnerMostStructureOffset(
std::string BINARYNINJAUIAPI createStructureName(BinaryNinja::TypeContainer types, const std::string& prefix = "struct_");
std::optional<BinaryNinja::Variable> BINARYNINJAUIAPI getSplitVariableForAssignment(
- FunctionRef func, BNFunctionGraphType ilType, uint64_t location, const BinaryNinja::Variable& var);
+ FunctionRef func, const BinaryNinja::FunctionViewType& ilType, uint64_t location, const BinaryNinja::Variable& var);
std::optional<size_t> getVariableDefinitionInstructionIndex(
- FunctionRef func, BNFunctionGraphType funcType, const BinaryNinja::Variable& var, size_t ilInstructionIndex);
+ FunctionRef func, const BinaryNinja::FunctionViewType& funcType, const BinaryNinja::Variable& var, size_t ilInstructionIndex);
std::optional<size_t> getVariableDefinitionAddress(
- FunctionRef func, BNFunctionGraphType funcType, const BinaryNinja::Variable& var, size_t ilInstructionIndex);
+ FunctionRef func, const BinaryNinja::FunctionViewType& funcType, const BinaryNinja::Variable& var, size_t ilInstructionIndex);
bool IsDefaultArgumentOrParameterName(const std::string& name);
std::optional<std::string> GetVariableNameFromExpr(BinaryNinja::Function* func,
diff --git a/ui/disassemblyview.h b/ui/disassemblyview.h
index 3e0408ae..c7595f96 100644
--- a/ui/disassemblyview.h
+++ b/ui/disassemblyview.h
@@ -31,11 +31,11 @@
*/
class BINARYNINJAUIAPI DisassemblyHistoryEntry : public FlowGraphHistoryEntry
{
- BNFunctionGraphType m_graphType;
+ BinaryNinja::FunctionViewType m_graphType;
public:
- BNFunctionGraphType getGraphType() const { return m_graphType; }
- void setGraphType(BNFunctionGraphType type) { m_graphType = type; }
+ const BinaryNinja::FunctionViewType& getGraphType() const { return m_graphType; }
+ void setGraphType(const BinaryNinja::FunctionViewType& type) { m_graphType = type; }
virtual Json::Value serialize() const override;
virtual bool deserialize(const Json::Value& value) override;
@@ -71,8 +71,8 @@ class BINARYNINJAUIAPI DisassemblyView : public FlowGraphWidget
virtual ViewPaneHeaderSubtypeWidget* getHeaderSubtypeWidget() override;
virtual QWidget* getHeaderOptionsWidget() override;
- virtual BNFunctionGraphType getILViewType() override { return m_ilViewType; };
- virtual void setILViewType(BNFunctionGraphType ilViewType) override;
+ virtual BinaryNinja::FunctionViewType getILViewType() override { return m_ilViewType; };
+ virtual void setILViewType(const BinaryNinja::FunctionViewType& ilViewType) override;
void setOption(BNDisassemblyOption option, bool state = true);
void toggleOption(BNDisassemblyOption option);
@@ -132,7 +132,7 @@ class BINARYNINJAUIAPI DisassemblyView : public FlowGraphWidget
void bindActions();
static void addOptionsMenuActions(Menu& menu);
- BNFunctionGraphType m_ilViewType;
+ BinaryNinja::FunctionViewType m_ilViewType;
std::set<BNDisassemblyOption> m_options;
BNDisassemblyAddressMode m_addressMode;
BNDisassemblyCallParameterHints m_callParamHints;
@@ -202,7 +202,7 @@ class BINARYNINJAUIAPI DisassemblyFunctionHeader : public QWidget
void updateFonts();
void setCurrentFunction(FunctionRef func);
- void setILViewType(BNFunctionGraphType ilViewType);
+ void setILViewType(const BinaryNinja::FunctionViewType& ilViewType);
void setHighlightToken(const HighlightTokenState& state);
void notifyRefresh();
@@ -235,7 +235,7 @@ class BINARYNINJAUIAPI DisassemblyContainer : public QWidget, public ViewContain
void updateFonts();
void refreshHeader(FunctionRef func);
void setCurrentFunction(FunctionRef func);
- void setILViewType(BNFunctionGraphType ilViewType);
+ void setILViewType(const BinaryNinja::FunctionViewType& ilViewType);
void setHeaderHighlightToken(const HighlightTokenState& state);
protected:
diff --git a/ui/ilchooser.h b/ui/ilchooser.h
index e57b82a6..561e264f 100644
--- a/ui/ilchooser.h
+++ b/ui/ilchooser.h
@@ -22,10 +22,10 @@ class BINARYNINJAUIAPI ILChooserWidget : public MenuHelper
public:
ILChooserWidget(QWidget* parent, UIActionHandler* handler, bool longDescription);
- void updateStatus(BNFunctionGraphType current);
+ void updateStatus(const BinaryNinja::FunctionViewType& current);
- static QString shortNameForILType(BNFunctionGraphType type);
- static QString longNameForILType(BNFunctionGraphType type);
+ static QString shortNameForILType(const BinaryNinja::FunctionViewType& type);
+ static QString longNameForILType(const BinaryNinja::FunctionViewType& type);
protected:
virtual void showMenu() override;
diff --git a/ui/linearview.h b/ui/linearview.h
index 8dc49041..1c470cb6 100644
--- a/ui/linearview.h
+++ b/ui/linearview.h
@@ -125,12 +125,14 @@ class StickyHeader: public QWidget
uint64_t m_gutterWidthChars;
LinearViewLine m_line;
+ BinaryNinja::FunctionViewType m_viewType;
QProgressIndicator* m_updateIndicator;
public:
StickyHeader(BinaryViewRef data, LinearView* parent);
void updateLine(const LinearViewLine& line);
+ void updateViewType(const BinaryNinja::FunctionViewType& viewType);
void updateFonts();
virtual void paintEvent(QPaintEvent* event) override;
@@ -209,7 +211,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
SettingsRef m_settings;
DisassemblySettingsRef m_options;
- BNFunctionGraphType m_ilViewType, m_prevILViewType = InvalidILViewType;
+ BinaryNinja::FunctionViewType m_ilViewType, m_prevILViewType = InvalidILViewType;
HexEditorHighlightState m_highlightState;
bool m_singleFunctionView = false;
@@ -476,8 +478,8 @@ public:
virtual HighlightTokenState getHighlightTokenState() override { return m_highlight; }
void setHighlightTokenState(const HighlightTokenState& hts);
- virtual BNFunctionGraphType getILViewType() override { return m_ilViewType; };
- virtual void setILViewType(BNFunctionGraphType ilViewType) override;
+ virtual BinaryNinja::FunctionViewType getILViewType() override { return m_ilViewType; };
+ virtual void setILViewType(const BinaryNinja::FunctionViewType& ilViewType) override;
void setHighlightMode(HexEditorHighlightMode mode);
void setColorMode(HexEditorColorMode mode);
diff --git a/ui/util.h b/ui/util.h
index f1a68752..6e81a479 100644
--- a/ui/util.h
+++ b/ui/util.h
@@ -35,7 +35,7 @@ PlatformRef BINARYNINJAUIAPI getOrAskForPlatform(QWidget* parent, BinaryViewRef
PlatformRef BINARYNINJAUIAPI getOrAskForPlatform(QWidget* parent, PlatformRef defaultValue);
std::optional<std::string> BINARYNINJAUIAPI getStringForGraphType(BNFunctionGraphType type);
-std::optional<BNFunctionGraphType> BINARYNINJAUIAPI getGraphTypeForString(const std::string& type);
+std::optional<BinaryNinja::FunctionViewType> BINARYNINJAUIAPI getGraphTypeForString(const std::string& type);
namespace fmt
{
diff --git a/ui/variablelist.h b/ui/variablelist.h
index 2b7ed8e7..e10d40eb 100644
--- a/ui/variablelist.h
+++ b/ui/variablelist.h
@@ -101,7 +101,7 @@ class BINARYNINJAUIAPI VariableListModel : public QAbstractListModel
ViewFrame* m_view;
BinaryViewRef m_data;
FunctionRef m_func;
- BNFunctionGraphType m_funcType;
+ BinaryNinja::FunctionViewType m_funcType;
bool m_funcExceedsComplexity = false;
BinaryNinja::AdvancedFunctionAnalysisDataRequestor m_analysisRequestor;
std::vector<VariableListItem> m_items;
@@ -123,13 +123,13 @@ class BINARYNINJAUIAPI VariableListModel : public QAbstractListModel
FunctionRef function() const;
//! Get the current function type.
- BNFunctionGraphType functionType() const;
+ const BinaryNinja::FunctionViewType& functionType() const;
//! Whether or not the function exceeds the set complexity threshold
bool functionExceedsComplexity() const { return m_funcExceedsComplexity; }
//! Set the focused function and update the content of the list.
- void setFunction(FunctionRef func, BNFunctionGraphType il, const HighlightTokenState& hts);
+ void setFunction(FunctionRef func, const BinaryNinja::FunctionViewType& il, const HighlightTokenState& hts);
//! Set the selection model, should correspond to the parent widget's.
void setSelectionModel(QItemSelectionModel* model);
diff --git a/ui/viewframe.h b/ui/viewframe.h
index 260939f0..234e01c9 100644
--- a/ui/viewframe.h
+++ b/ui/viewframe.h
@@ -150,21 +150,21 @@ class BINARYNINJAUIAPI View
virtual bool findNextData(uint64_t start, uint64_t end, const BinaryNinja::DataBuffer& data, uint64_t& addr,
BNFindFlag flags, const std::function<bool(size_t current, size_t total)>& cb);
virtual bool findNextText(uint64_t start, uint64_t end, const std::string& text, uint64_t& addr,
- DisassemblySettingsRef settings, BNFindFlag flags, BNFunctionGraphType graph,
+ DisassemblySettingsRef settings, BNFindFlag flags, const BinaryNinja::FunctionViewType& graph,
const std::function<bool(size_t current, size_t total)>& cb);
virtual bool findNextConstant(uint64_t start, uint64_t end, uint64_t constant, uint64_t& addr,
- DisassemblySettingsRef settings, BNFunctionGraphType graph,
+ DisassemblySettingsRef settings, const BinaryNinja::FunctionViewType& graph,
const std::function<bool(size_t current, size_t total)>& cb);
virtual bool findAllData(uint64_t start, uint64_t end, const BinaryNinja::DataBuffer& data, BNFindFlag flags,
const std::function<bool(size_t current, size_t total)>& cb,
const std::function<bool(uint64_t addr, const BinaryNinja::DataBuffer& match)>& matchCallback);
virtual bool findAllText(uint64_t start, uint64_t end, const std::string& data, DisassemblySettingsRef settings,
- BNFindFlag flags, BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& cb,
+ BNFindFlag flags, const BinaryNinja::FunctionViewType& graph, const std::function<bool(size_t current, size_t total)>& cb,
const std::function<bool(
uint64_t addr, const std::string& match, const BinaryNinja::LinearDisassemblyLine& line)>& matchCallback);
virtual bool findAllConstant(uint64_t start, uint64_t end, uint64_t constant, DisassemblySettingsRef settings,
- BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& cb,
+ const BinaryNinja::FunctionViewType& graph, const std::function<bool(size_t current, size_t total)>& cb,
const std::function<bool(uint64_t addr, const BinaryNinja::LinearDisassemblyLine& line)>& matchCallback);
virtual BinaryViewRef getData() = 0;
@@ -228,8 +228,8 @@ class BINARYNINJAUIAPI View
virtual LowLevelILFunctionRef getCurrentLowLevelILFunction() { return nullptr; }
virtual MediumLevelILFunctionRef getCurrentMediumLevelILFunction() { return nullptr; }
virtual HighLevelILFunctionRef getCurrentHighLevelILFunction() { return nullptr; }
- virtual BNFunctionGraphType getILViewType() { return InvalidILViewType; }
- virtual void setILViewType(BNFunctionGraphType ilViewType) {}
+ virtual BinaryNinja::FunctionViewType getILViewType() { return InvalidILViewType; }
+ virtual void setILViewType(const BinaryNinja::FunctionViewType& ilViewType) {}
virtual size_t getCurrentILInstructionIndex() { return BN_INVALID_EXPR; }
virtual size_t getSelectionStartILInstructionIndex() { return BN_INVALID_EXPR; }
virtual BNILIndexRange getILIndexRange() { return {BN_INVALID_EXPR, BN_INVALID_EXPR}; }
@@ -290,25 +290,25 @@ class BINARYNINJAUIAPI ViewLocation
QString m_viewType;
FunctionRef m_function = nullptr;
uint64_t m_offset = 0;
- BNFunctionGraphType m_ilViewType = InvalidILViewType;
+ BinaryNinja::FunctionViewType m_ilViewType = InvalidILViewType;
size_t m_instrIndex = BN_INVALID_EXPR;
public:
ViewLocation() {}
ViewLocation(const QString& viewType, uint64_t offset) : m_valid(true), m_viewType(viewType), m_offset(offset) {}
- ViewLocation(const QString& viewType, uint64_t offset, BNFunctionGraphType ilViewType) :
+ ViewLocation(const QString& viewType, uint64_t offset, const BinaryNinja::FunctionViewType& ilViewType) :
m_valid(true), m_viewType(viewType), m_offset(offset), m_ilViewType(ilViewType)
{}
- ViewLocation(const QString& viewType, uint64_t offset, BNFunctionGraphType ilViewType, size_t instrIndex) :
+ ViewLocation(const QString& viewType, uint64_t offset, const BinaryNinja::FunctionViewType& ilViewType, size_t instrIndex) :
m_valid(true), m_viewType(viewType), m_offset(offset), m_ilViewType(ilViewType), m_instrIndex(instrIndex)
{}
- ViewLocation(const QString& viewType, FunctionRef function, uint64_t offset, BNFunctionGraphType ilViewType,
+ ViewLocation(const QString& viewType, FunctionRef function, uint64_t offset, const BinaryNinja::FunctionViewType& ilViewType,
size_t instrIndex) :
m_valid(true),
m_viewType(viewType), m_function(function), m_offset(offset), m_ilViewType(ilViewType), m_instrIndex(instrIndex)
{}
ViewLocation(
- FunctionRef function, uint64_t offset, BNFunctionGraphType ilViewType, size_t instrIndex = BN_INVALID_EXPR) :
+ FunctionRef function, uint64_t offset, const BinaryNinja::FunctionViewType& ilViewType, size_t instrIndex = BN_INVALID_EXPR) :
m_valid(true),
m_function(function), m_offset(offset), m_ilViewType(ilViewType), m_instrIndex(instrIndex)
{}
@@ -316,13 +316,13 @@ class BINARYNINJAUIAPI ViewLocation
bool isValid() const { return m_valid; }
QString getViewType() const { return m_viewType; }
uint64_t getOffset() const { return m_offset; }
- BNFunctionGraphType getILViewType() const { return m_ilViewType; }
+ const BinaryNinja::FunctionViewType& getILViewType() const { return m_ilViewType; }
size_t getInstrIndex() const { return m_instrIndex; }
FunctionRef getFunction() const { return m_function; }
void setViewType(const QString& viewType) { m_viewType = viewType; }
void setOffset(uint64_t offset) { m_offset = offset; }
- void setILViewType(BNFunctionGraphType ilViewType) { m_ilViewType = ilViewType; }
+ void setILViewType(const BinaryNinja::FunctionViewType& ilViewType) { m_ilViewType = ilViewType; }
void setInstrIndex(uint64_t index) { m_instrIndex = index; }
void setFunction(FunctionRef function) { m_function = function; }
diff --git a/ui/xreflist.h b/ui/xreflist.h
index eb4edc7e..e9df2df8 100644
--- a/ui/xreflist.h
+++ b/ui/xreflist.h
@@ -304,7 +304,7 @@ class BINARYNINJAUIAPI CrossReferenceTreeModel : public QAbstractItemModel
ViewFrame* m_view;
std::vector<XrefItem> m_refs;
size_t m_maxUIItems;
- std::optional<BNFunctionGraphType> m_graphType;
+ std::optional<BinaryNinja::FunctionViewType> m_graphType;
SelectionInfoForXref m_curRef;
QTimer* m_updateTimer;
@@ -328,7 +328,7 @@ class BINARYNINJAUIAPI CrossReferenceTreeModel : public QAbstractItemModel
ViewFrame* getView() const { return m_view; }
virtual void updateMaxUIItems(size_t value) { m_maxUIItems = value; }
size_t getMaxUIItems() const { return m_maxUIItems; }
- void setGraphType(BNFunctionGraphType type);
+ void setGraphType(const BinaryNinja::FunctionViewType& type);
void requestAdvancedAnalysis();
Q_SIGNALS:
@@ -352,7 +352,7 @@ class BINARYNINJAUIAPI CrossReferenceTableModel : public QAbstractTableModel
ViewFrame* m_view;
std::vector<XrefItem> m_refs;
size_t m_maxUIItems;
- std::optional<BNFunctionGraphType> m_graphType;
+ std::optional<BinaryNinja::FunctionViewType> m_graphType;
SelectionInfoForXref m_curRef;
QTimer* m_updateTimer;
@@ -393,7 +393,7 @@ class BINARYNINJAUIAPI CrossReferenceTableModel : public QAbstractTableModel
ViewFrame* getView() const { return m_view; }
virtual void updateMaxUIItems(size_t value) { m_maxUIItems = value; }
size_t getMaxUIItems() const { return m_maxUIItems; }
- void setGraphType(BNFunctionGraphType type);
+ void setGraphType(const BinaryNinja::FunctionViewType& type);
void requestAdvancedAnalysis();
Q_SIGNALS:
void needRepaint();
@@ -530,7 +530,7 @@ public:
virtual int filteredCount() const override;
void updateTextFilter(const QString& filterText);
virtual void updateMaxUIItems(size_t count) override;
- void setGraphType(BNFunctionGraphType type) { m_tree->setGraphType(type); }
+ void setGraphType(const BinaryNinja::FunctionViewType& type) { m_tree->setGraphType(type); }
virtual void OnAnalysisFunctionUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override;
Q_SIGNALS:
@@ -574,7 +574,7 @@ class BINARYNINJAUIAPI CrossReferenceTable : public QTableView, public CrossRefe
virtual int leafCount() const override;
virtual int filteredCount() const override;
virtual void updateMaxUIItems(size_t count) override;
- void setGraphType(BNFunctionGraphType type) { m_table->setGraphType(type); }
+ void setGraphType(const BinaryNinja::FunctionViewType& type) { m_table->setGraphType(type); }
virtual void OnAnalysisFunctionUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override;
public Q_SLOTS:
@@ -609,7 +609,7 @@ class BINARYNINJAUIAPI CrossReferenceWidget : public SidebarWidget, public UICon
CrossReferenceContainer* m_container;
bool m_useTableView;
Qt::Orientation m_primaryOrientation;
- std::optional<BNFunctionGraphType> m_graphType;
+ std::optional<BinaryNinja::FunctionViewType> m_graphType;
QTimer* m_hoverTimer;
QPoint m_hoverPos;
@@ -656,7 +656,7 @@ class BINARYNINJAUIAPI CrossReferenceWidget : public SidebarWidget, public UICon
bool tableView() const { return m_useTableView; }
bool uiMaxItemsExceeded() const { return m_uiMaxItemsExceeded; }
void setUIMaxItemsExceeded(bool value) { m_uiMaxItemsExceeded = value; }
- void setGraphType(BNFunctionGraphType type);
+ void setGraphType(const BinaryNinja::FunctionViewType& type);
virtual void focus() override;