From 217b4a3361cbf62020658f12efb47b8fabb6a24d Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Fri, 23 Jan 2026 18:36:13 -0500 Subject: Initial support for Go and Pascal calling conventions --- arch/x86/arch_x86.cpp | 277 +++++++++++++++++++++++++++++++++++++++++ binaryninjaapi.h | 11 ++ binaryninjacore.h | 17 ++- callingconvention.cpp | 111 +++++++++++++++++ docs/guide/types/attributes.md | 12 ++ python/callingconvention.py | 102 +++++++++++++++ rust/src/calling_convention.rs | 3 + 7 files changed, 529 insertions(+), 4 deletions(-) diff --git a/arch/x86/arch_x86.cpp b/arch/x86/arch_x86.cpp index 83a476bf..98454dd9 100644 --- a/arch/x86/arch_x86.cpp +++ b/arch/x86/arch_x86.cpp @@ -3971,6 +3971,170 @@ public: }; +class X86PascalCallingConvention : public X86BaseCallingConvention +{ +public: + X86PascalCallingConvention(Architecture* arch) : X86BaseCallingConvention(arch, "pascal") {} + + bool IsNonRegisterArgumentIndirect(BinaryView*, Type* type) override + { + return type && !type->IsFloat() && type->GetWidth() > 4; + } + + bool IsStackAdjustedOnReturn() override + { + return true; + } + + bool AreStackArgumentsPushedLeftToRight() override + { + return true; + } + + Variable GetIndirectReturnValueLocation() override + { + // Return value pointer is always at the top of the stack (effectively the last parameter + // in a left-to-right convention) + return Variable::StackOffset(4); + } + + std::optional GetReturnedIndirectReturnValuePointer() override + { + return std::nullopt; + } +}; + + +class X86PascalRegisterCallingConvention : public X86BaseCallingConvention +{ +public: + X86PascalRegisterCallingConvention(Architecture* arch) : X86BaseCallingConvention(arch, "register") {} + + vector GetIntegerArgumentRegisters() override + { + return { XED_REG_EAX, XED_REG_EDX, XED_REG_ECX }; + } + + bool IsNonRegisterArgumentIndirect(BinaryView*, Type* type) override + { + return type && !type->IsFloat() && type->GetWidth() > 4; + } + + bool AreStackArgumentsPushedLeftToRight() override + { + return true; + } + + std::optional GetReturnedIndirectReturnValuePointer() override + { + return std::nullopt; + } +}; + + +class X86GoStackCallingConvention: public CallingConvention +{ +public: + X86GoStackCallingConvention(Architecture* arch): CallingConvention(arch, "go-stack") + { + } + + bool IsEligibleForHeuristics() override + { + // This convention cannot be detected by heuristics at this time and will cause issues + // with non-Go code. + return false; + } + + uint32_t GetIntegerReturnValueRegister() override + { + return BN_INVALID_REGISTER; + } + + vector GetCallerSavedRegisters() override + { + return vector { XED_REG_EAX, XED_REG_ECX, XED_REG_EDX, XED_REG_EBX, XED_REG_EBP }; + } + + RegisterValue GetIncomingFlagValue(uint32_t flag, Function*) override + { + RegisterValue result; + if (flag == IL_FLAG_D) + { + result.state = ConstantValue; + result.value = 0; + } + return result; + } + + ValueLocation GetReturnValueLocation(BinaryView*, const ReturnValue&) override + { + // It is not possible for this API to determine the return value location on the stack at + // this point, return an invalid location and fall back to GetCallLayout. + return ValueLocation(); + } + + CallLayout GetCallLayout(BinaryView* view, const ReturnValue& returnValue, const vector& params, + const std::optional>& permittedRegs) override + { + CallLayout result; + result.parameters = GetParameterLocations(view, result.returnValue, params, permittedRegs); + + if (returnValue.type.GetValue() && returnValue.type->GetClass() != VoidTypeClass) + { + if (returnValue.defaultLocation) + { + int64_t stackOffset = 4; + size_t i = 0; + for (auto it = result.parameters.begin(); it != result.parameters.end(); ++i, ++it) + { + std::optional varStorage; + std::optional varSize; + for (auto& component: it->components) + { + if (component.variable.type != StackVariableSourceType) + continue; + if (!varStorage.has_value() || component.variable.storage > varStorage.value()) + { + varStorage = component.variable.storage; + if (!it->indirect) + varSize = component.size; + } + } + + if (!varStorage.has_value() || varStorage.value() < stackOffset) + continue; + if (it->indirect) + varSize = 4; + + size_t width = 4; + if (varSize.has_value()) + width = varSize.value(); + else if (i < params.size() && params[i].type.GetValue()) + width = params[i].type->GetWidth(); + + if (width < 4) + width = 4; + else if ((width % 4) != 0) + width += 4 - (width % 4); + + stackOffset = varStorage.value() + width; + } + + result.returnValue = Variable::StackOffset(stackOffset); + } + else + { + result.returnValue = returnValue.location.GetValue(); + } + } + + result.registerStackAdjustments = GetRegisterStackAdjustments(view, result.returnValue, result.parameters); + return result; + } +}; + + class X64BaseCallingConvention: public CallingConvention { public: @@ -4145,6 +4309,111 @@ public: }; +class X64GoStackCallingConvention: public CallingConvention +{ +public: + X64GoStackCallingConvention(Architecture* arch): CallingConvention(arch, "go-stack") + { + } + + bool IsEligibleForHeuristics() override + { + // This convention cannot be detected by heuristics at this time and will cause issues + // with non-Go code. + return false; + } + + uint32_t GetIntegerReturnValueRegister() override + { + return BN_INVALID_REGISTER; + } + + vector GetCallerSavedRegisters() override + { + return vector { XED_REG_RAX, XED_REG_RCX, XED_REG_RDX, XED_REG_RBX, XED_REG_RBP, + XED_REG_R8, XED_REG_R9, XED_REG_R10, XED_REG_R11, XED_REG_R12, XED_REG_R13, XED_REG_R14, + XED_REG_R15 }; + } + + RegisterValue GetIncomingFlagValue(uint32_t flag, Function*) override + { + RegisterValue result; + if (flag == IL_FLAG_D) + { + result.state = ConstantValue; + result.value = 0; + } + return result; + } + + ValueLocation GetReturnValueLocation(BinaryView*, const ReturnValue&) override + { + // It is not possible for this API to determine the return value location on the stack at + // this point, return an invalid location and fall back to GetCallLayout. + return ValueLocation(); + } + + CallLayout GetCallLayout(BinaryView* view, const ReturnValue& returnValue, const vector& params, + const std::optional>& permittedRegs) override + { + CallLayout result; + result.parameters = GetParameterLocations(view, result.returnValue, params, permittedRegs); + + if (returnValue.type.GetValue() && returnValue.type->GetClass() != VoidTypeClass) + { + if (returnValue.defaultLocation) + { + int64_t stackOffset = 8; + size_t i = 0; + for (auto it = result.parameters.begin(); it != result.parameters.end(); ++i, ++it) + { + std::optional varStorage; + std::optional varSize; + for (auto& component: it->components) + { + if (component.variable.type != StackVariableSourceType) + continue; + if (!varStorage.has_value() || component.variable.storage > varStorage.value()) + { + varStorage = component.variable.storage; + if (!it->indirect) + varSize = component.size; + } + } + + if (!varStorage.has_value() || varStorage.value() < stackOffset) + continue; + if (it->indirect) + varSize = 8; + + size_t width = 8; + if (varSize.has_value()) + width = varSize.value(); + else if (i < params.size() && params[i].type.GetValue()) + width = params[i].type->GetWidth(); + + if (width < 8) + width = 8; + else if ((width % 8) != 0) + width += 8 - (width % 8); + + stackOffset = varStorage.value() + width; + } + + result.returnValue = Variable::StackOffset(stackOffset); + } + else + { + result.returnValue = returnValue.location.GetValue(); + } + } + + result.registerStackAdjustments = GetRegisterStackAdjustments(view, result.returnValue, result.parameters); + return result; + } +}; + + class x86MachoRelocationHandler: public RelocationHandler { public: @@ -5052,6 +5321,12 @@ extern "C" x86->RegisterCallingConvention(conv); conv = new X86LinuxSystemCallConvention(x86); x86->RegisterCallingConvention(conv); + conv = new X86PascalCallingConvention(x86); + x86->RegisterCallingConvention(conv); + conv = new X86PascalRegisterCallingConvention(x86); + x86->RegisterCallingConvention(conv); + conv = new X86GoStackCallingConvention(x86); + x86->RegisterCallingConvention(conv); x86->RegisterRelocationHandler("Mach-O", new x86MachoRelocationHandler()); x86->RegisterRelocationHandler("KCView", new x86MachoRelocationHandler()); @@ -5069,6 +5344,8 @@ extern "C" x64->RegisterCallingConvention(conv); conv = new X64LinuxSystemCallConvention(x64); x64->RegisterCallingConvention(conv); + conv = new X64GoStackCallingConvention(x64); + x64->RegisterCallingConvention(conv); x64->RegisterRelocationHandler("Mach-O", new x64MachoRelocationHandler()); x64->RegisterRelocationHandler("KCView", new x64MachoRelocationHandler()); diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 1029d33b..70c6552f 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -17991,6 +17991,7 @@ namespace BinaryNinja { static bool IsArgumentTypeRegisterCompatibleCallback(void* ctxt, BNBinaryView* view, BNType* type); static bool IsNonRegisterArgumentIndirectCallback(void* ctxt, BNBinaryView* view, BNType* type); static bool AreStackArgumentsNaturallyAlignedCallback(void* ctxt); + static bool AreStackArgumentsPushedLeftToRightCallback(void* ctxt); static void GetCallLayoutCallback(void* ctxt, BNBinaryView* view, BNReturnValue* returnValue, BNFunctionParameter* params, size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, @@ -18003,6 +18004,9 @@ namespace BinaryNinja { BNValueLocation* returnValue, BNFunctionParameter* params, size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, size_t permittedRegCount, size_t* outLocationCount); static void FreeParameterLocationsCallback(void* ctxt, BNValueLocation* locations, size_t count); + static BNVariable* GetParameterOrderingForVariablesCallback( + void* ctxt, BNBinaryView* view, BNVariable* vars, BNType** types, size_t paramCount, size_t* outCount); + static void FreeVariableListCallback(void* ctxt, BNVariable* vars, size_t count); static int64_t GetStackAdjustmentForLocationsCallback(void* ctxt, BNBinaryView* view, BNValueLocation* returnValue, BNValueLocation* locations, BNType** types, size_t paramCount); static size_t GetRegisterStackAdjustmentsCallback(void* ctxt, BNBinaryView* view, BNValueLocation* returnValue, @@ -18061,6 +18065,7 @@ namespace BinaryNinja { bool DefaultIsArgumentTypeRegisterCompatible(Type* type); virtual bool IsNonRegisterArgumentIndirect(BinaryView* view, Type* type); virtual bool AreStackArgumentsNaturallyAligned(); + virtual bool AreStackArgumentsPushedLeftToRight(); virtual CallLayout GetCallLayout(BinaryView* view, const ReturnValue& returnValue, const std::vector& params, @@ -18069,6 +18074,8 @@ namespace BinaryNinja { virtual std::vector GetParameterLocations(BinaryView* view, const std::optional& returnValue, const std::vector& params, const std::optional>& permittedRegs = std::nullopt); + virtual std::vector GetParameterOrderingForVariables( + BinaryView* view, const std::map>& params); virtual int64_t GetStackAdjustmentForLocations(BinaryView* view, const std::optional& returnValue, const std::vector& locations, const std::vector>& types); @@ -18082,6 +18089,7 @@ namespace BinaryNinja { std::vector GetDefaultParameterLocations(BinaryView* view, const std::optional& returnValue, const std::vector& params, const std::optional>& permittedRegs = std::nullopt); + std::vector GetDefaultParameterOrderingForVariables(const std::map>& params); int64_t GetDefaultStackAdjustmentForLocations(const std::optional& returnValue, const std::vector& locations, const std::vector>& types); std::map GetDefaultRegisterStackAdjustments( @@ -18128,6 +18136,7 @@ namespace BinaryNinja { virtual bool IsArgumentTypeRegisterCompatible(BinaryView* view, Type* type) override; virtual bool IsNonRegisterArgumentIndirect(BinaryView* view, Type* type) override; virtual bool AreStackArgumentsNaturallyAligned() override; + virtual bool AreStackArgumentsPushedLeftToRight() override; virtual CallLayout GetCallLayout(BinaryView* view, const ReturnValue& returnValue, const std::vector& params, @@ -18136,6 +18145,8 @@ namespace BinaryNinja { virtual std::vector GetParameterLocations(BinaryView* view, const std::optional& returnValue, const std::vector& params, const std::optional>& permittedRegs = std::nullopt) override; + virtual std::vector GetParameterOrderingForVariables( + BinaryView* view, const std::map>& params) override; virtual int64_t GetStackAdjustmentForLocations(BinaryView* view, const std::optional& returnValue, const std::vector& locations, const std::vector>& types) override; diff --git a/binaryninjacore.h b/binaryninjacore.h index fd356b41..033d3c29 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1157,7 +1157,10 @@ extern "C" ILTransparentCopy = 0x1000, // Instruction is defining an implicit trait of the calling convention - MLILCallingConventionImplicit = 0x2000 + MLILCallingConventionImplicit = 0x2000, + + // Function call returns its result onto the caller's stack + ILStackReturn = 0x4000, }; BN_ENUM(uint8_t, BNIntrinsicClass) @@ -2966,6 +2969,7 @@ extern "C" bool (*isArgumentTypeRegisterCompatible)(void* ctxt, BNBinaryView* view, BNType* type); bool (*isNonRegisterArgumentIndirect)(void* ctxt, BNBinaryView* view, BNType* type); bool (*areStackArgumentsNaturallyAligned)(void* ctxt); + bool (*areStackArgumentsPushedLeftToRight)(void* ctxt); void (*getIncomingVariableForParameterVariable)( void* ctxt, const BNVariable* var, BNFunction* func, BNVariable* result); @@ -2985,6 +2989,9 @@ extern "C" BNFunctionParameter* params, size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, size_t permittedRegCount, size_t* outLocationCount); void (*freeParameterLocations)(void* ctxt, BNValueLocation* locations, size_t count); + BNVariable* (*getParameterOrderingForVariables)( + void* ctxt, BNBinaryView* view, BNVariable* vars, BNType** types, size_t paramCount, size_t* outCount); + void (*freeVariableList)(void* ctxt, BNVariable* vars, size_t count); int64_t (*getStackAdjustmentForLocations)(void* ctxt, BNBinaryView* view, BNValueLocation* returnValue, BNValueLocation* locations, BNType** types, size_t paramCount); size_t (*getRegisterStackAdjustments)(void* ctxt, BNBinaryView* view, BNValueLocation* returnValue, @@ -7792,9 +7799,10 @@ extern "C" size_t* outCount); BINARYNINJACOREAPI void BNFreeValueLocationList(BNValueLocation* locations, size_t count); - BINARYNINJACOREAPI BNVariable* BNGetParameterOrderingForVariables( - BNCallingConvention* cc, const BNVariable* paramVars, const BNType** paramTypes, - size_t paramCount, size_t* count); + BINARYNINJACOREAPI BNVariable* BNGetParameterOrderingForVariables(BNCallingConvention* cc, BNBinaryView* view, + const BNVariable* paramVars, const BNType** paramTypes, size_t paramCount, size_t* count); + BINARYNINJACOREAPI BNVariable* BNGetDefaultParameterOrderingForVariables(BNCallingConvention* cc, + const BNVariable* paramVars, const BNType** paramTypes, size_t paramCount, size_t* count); BINARYNINJACOREAPI int64_t BNGetStackAdjustmentForLocations(BNCallingConvention* cc, BNBinaryView* view, BNValueLocation* returnValue, const BNValueLocation* paramLocations, const BNType** paramTypes, size_t paramCount); @@ -7828,6 +7836,7 @@ extern "C" BINARYNINJACOREAPI bool BNDefaultIsArgumentTypeRegisterCompatible(BNCallingConvention* cc, BNType* type); BINARYNINJACOREAPI bool BNIsNonRegisterArgumentIndirect(BNCallingConvention* cc, BNBinaryView* view, BNType* type); BINARYNINJACOREAPI bool BNAreStackArgumentsNaturallyAligned(BNCallingConvention* cc); + BINARYNINJACOREAPI bool BNAreStackArgumentsPushedLeftToRight(BNCallingConvention* cc); BINARYNINJACOREAPI BNCallingConvention* BNGetArchitectureDefaultCallingConvention(BNArchitecture* arch); BINARYNINJACOREAPI BNCallingConvention* BNGetArchitectureCdeclCallingConvention(BNArchitecture* arch); diff --git a/callingconvention.cpp b/callingconvention.cpp index 66b814f1..ec32a5b1 100644 --- a/callingconvention.cpp +++ b/callingconvention.cpp @@ -117,12 +117,15 @@ CallingConvention::CallingConvention(Architecture* arch, const string& name) cc.isArgumentTypeRegisterCompatible = IsArgumentTypeRegisterCompatibleCallback; cc.isNonRegisterArgumentIndirect = IsNonRegisterArgumentIndirectCallback; cc.areStackArgumentsNaturallyAligned = AreStackArgumentsNaturallyAlignedCallback; + cc.areStackArgumentsPushedLeftToRight = AreStackArgumentsPushedLeftToRightCallback; cc.getCallLayout = GetCallLayoutCallback; cc.freeCallLayout = FreeCallLayoutCallback; cc.getReturnValueLocation = GetReturnValueLocationCallback; cc.freeValueLocation = FreeValueLocationCallback; cc.getParameterLocations = GetParameterLocationsCallback; cc.freeParameterLocations = FreeParameterLocationsCallback; + cc.getParameterOrderingForVariables = GetParameterOrderingForVariablesCallback; + cc.freeVariableList = FreeVariableListCallback; cc.getStackAdjustmentForLocations = GetStackAdjustmentForLocationsCallback; cc.getRegisterStackAdjustments = GetRegisterStackAdjustmentsCallback; cc.freeRegisterStackAdjustments = FreeRegisterStackAdjustmentsCallback; @@ -406,6 +409,13 @@ bool CallingConvention::AreStackArgumentsNaturallyAlignedCallback(void* ctxt) } +bool CallingConvention::AreStackArgumentsPushedLeftToRightCallback(void* ctxt) +{ + CallbackRef cc(ctxt); + return cc->AreStackArgumentsPushedLeftToRight(); +} + + void CallingConvention::GetCallLayoutCallback(void* ctxt, BNBinaryView* view, BNReturnValue* returnValue, BNFunctionParameter* params, size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, size_t permittedRegCount, BNCallLayout* result) @@ -500,6 +510,33 @@ void CallingConvention::FreeParameterLocationsCallback(void*, BNValueLocation* l } +BNVariable* CallingConvention::GetParameterOrderingForVariablesCallback( + void* ctxt, BNBinaryView* view, BNVariable* vars, BNType** types, size_t paramCount, size_t* outCount) +{ + CallbackRef cc(ctxt); + Ref viewObj; + if (view) + viewObj = new BinaryView(BNNewViewReference(view)); + map> params; + for (size_t i = 0; i < paramCount; i++) + params[vars[i]] = types[i] ? new Type(BNNewTypeReference(types[i])) : nullptr; + + auto outVars = cc->GetParameterOrderingForVariables(viewObj, params); + + *outCount = outVars.size(); + BNVariable* result = new BNVariable[outVars.size()]; + for (size_t i = 0; i < outVars.size(); i++) + result[i] = outVars[i]; + return result; +} + + +void CallingConvention::FreeVariableListCallback(void*, BNVariable* vars, size_t) +{ + delete[] vars; +} + + int64_t CallingConvention::GetStackAdjustmentForLocationsCallback(void* ctxt, BNBinaryView* view, BNValueLocation* returnValue, BNValueLocation* locations, BNType** types, size_t paramCount) { @@ -750,6 +787,12 @@ bool CallingConvention::AreStackArgumentsNaturallyAligned() } +bool CallingConvention::AreStackArgumentsPushedLeftToRight() +{ + return false; +} + + CallLayout CallingConvention::GetCallLayout(BinaryView* view, const ReturnValue& returnValue, const vector& params, const optional>& permittedRegs) { @@ -771,6 +814,13 @@ vector CallingConvention::GetParameterLocations(BinaryView* view, } +std::vector CallingConvention::GetParameterOrderingForVariables( + BinaryView*, const std::map>& params) +{ + return GetDefaultParameterOrderingForVariables(params); +} + + int64_t CallingConvention::GetStackAdjustmentForLocations(BinaryView*, const std::optional& returnValue, const std::vector& locations, const std::vector>& types) { @@ -881,6 +931,33 @@ vector CallingConvention::GetDefaultParameterLocations(BinaryView } +std::vector CallingConvention::GetDefaultParameterOrderingForVariables( + const std::map>& params) +{ + BNVariable* vars = new BNVariable[params.size()]; + const BNType** types = new const BNType*[params.size()]; + size_t i = 0; + for (auto it = params.begin(); it != params.end(); ++it, ++i) + { + vars[i] = it->first; + types[i] = it->second.GetPtr() ? it->second->GetObject() : nullptr; + } + + size_t outCount = 0; + auto outVars = BNGetDefaultParameterOrderingForVariables(m_object, vars, types, params.size(), &outCount); + + delete[] vars; + delete[] types; + + vector result; + result.reserve(outCount); + for (i = 0; i < outCount; i++) + result.emplace_back(outVars[i]); + BNFreeVariableList(outVars); + return result; +} + + int64_t CallingConvention::GetDefaultStackAdjustmentForLocations(const std::optional& returnValue, const std::vector& locations, const std::vector>& types) { @@ -1145,6 +1222,12 @@ bool CoreCallingConvention::AreStackArgumentsNaturallyAligned() } +bool CoreCallingConvention::AreStackArgumentsPushedLeftToRight() +{ + return BNAreStackArgumentsPushedLeftToRight(m_object); +} + + CallLayout CoreCallingConvention::GetCallLayout(BinaryView* view, const ReturnValue& returnValue, const vector& params, const optional>& permittedRegs) { @@ -1241,6 +1324,34 @@ vector CoreCallingConvention::GetParameterLocations(BinaryView* v } +std::vector CoreCallingConvention::GetParameterOrderingForVariables( + BinaryView* view, const std::map>& params) +{ + BNVariable* vars = new BNVariable[params.size()]; + const BNType** types = new const BNType*[params.size()]; + size_t i = 0; + for (auto it = params.begin(); it != params.end(); ++it, ++i) + { + vars[i] = it->first; + types[i] = it->second.GetPtr() ? it->second->GetObject() : nullptr; + } + + size_t outCount = 0; + auto outVars = BNGetParameterOrderingForVariables( + m_object, view ? view->GetObject() : nullptr, vars, types, params.size(), &outCount); + + delete[] vars; + delete[] types; + + vector result; + result.reserve(outCount); + for (i = 0; i < outCount; i++) + result.emplace_back(outVars[i]); + BNFreeVariableList(outVars); + return result; +} + + int64_t CoreCallingConvention::GetStackAdjustmentForLocations(BinaryView* view, const std::optional& returnValue, const std::vector& locations, const std::vector>& types) diff --git a/docs/guide/types/attributes.md b/docs/guide/types/attributes.md index 34aa1221..1c674a0f 100644 --- a/docs/guide/types/attributes.md +++ b/docs/guide/types/attributes.md @@ -127,6 +127,18 @@ void takes_callback(int (__stdcall* param_func_ptr)()); void __convention("regparm") func(); ``` +### Built-in Calling Conventions + +The following built-in calling conventions without dedicated keywords are available in Binary Ninja: + +|Name|Valid architectures|Description| +|---|---|---| +|`linux-syscall`|Most|Linux system call| +|`windows-syscall`|aarch64|Windows system call| +|`apple-syscall`|aarch64|macOS and iOS system calls| +|`go-stack`|x86, x86_64|Stack-based calling convention used by the Go compiler on 32-bit x86 or older compilers| +|`register`|x86|Register-based calling convention with left-to-right parameter passing (used by default in Delphi)| + ## System Call Functions for Type Libraries [Type Libraries](typelibraries.md) can annotate system calls by adding functions with the special `__syscall()` attribute, specifying names and arguments for each syscall number. This attribute has no effect outside of [Type Libraries](typelibraries.md) and [Platform Types](platformtypes.md). diff --git a/python/callingconvention.py b/python/callingconvention.py index 4f17d1cf..14d9d0cc 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -104,10 +104,12 @@ class CallingConvention: global_pointer_reg = None implicitly_defined_regs = [] stack_args_naturally_aligned = False + stack_args_pushed_left_to_right = False _registered_calling_conventions = [] _pending_value_locations = {} _pending_value_location_lists = {} + _pending_variable_lists = {} _pending_reg_stack_adjustment_reg_lists = {} _pending_reg_stack_adjustment_amount_lists = {} @@ -191,12 +193,19 @@ class CallingConvention: self._cb.areStackArgumentsNaturallyAligned = self._cb.areStackArgumentsNaturallyAligned.__class__( self._are_stack_args_naturally_aligned ) + self._cb.areStackArgumentsPushedLeftToRight = self._cb.areStackArgumentsPushedLeftToRight.__class__( + self._are_stack_args_pushed_left_to_right + ) self._cb.getCallLayout = self._cb.getCallLayout.__class__(self._get_call_layout) self._cb.freeCallLayout = self._cb.freeCallLayout.__class__(self._free_call_layout) self._cb.getReturnValueLocation = self._cb.getReturnValueLocation.__class__(self._get_return_value_location) self._cb.freeValueLocation = self._cb.freeValueLocation.__class__(self._free_value_location) self._cb.getParameterLocations = self._cb.getParameterLocations.__class__(self._get_parameter_locations) self._cb.freeParameterLocations = self._cb.freeParameterLocations.__class__(self._free_parameter_locations) + self._cb.getParameterOrderingForVariables = self._cb.getParameterOrderingForVariables.__class__( + self._get_parameter_ordering_for_variables + ) + self._cb.freeVariableList = self._cb.freeVariableList.__class__(self._free_variable_list) self._cb.getStackAdjustmentForLocations = self._cb.getStackAdjustmentForLocations.__class__( self._get_stack_adjustment_for_locations ) @@ -551,6 +560,13 @@ class CallingConvention: log_error_for_exception("Unhandled Python exception in CallingConvention._are_stack_args_naturally_aligned") return False + def _are_stack_args_pushed_left_to_right(self, ctxt): + try: + return self.__class__.stack_args_pushed_left_to_right + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._are_stack_args_pushed_left_to_right") + return False + def _get_call_layout( self, ctxt, view, ret_value, params, param_count, has_permitted_regs, permitted_regs, permitted_reg_count, out_layout @@ -709,6 +725,45 @@ class CallingConvention: except: log_error_for_exception("Unhandled Python exception in CallingConvention._free_parameter_locations") + def _get_parameter_ordering_for_variables(self, ctxt, view, vars, type_list, param_count, out_count): + try: + if view: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + else: + view_obj = None + params = {} + for i in range(param_count): + var = variable.CoreVariable.from_BNVariable(vars[i]) + ty = types.Type.from_core_struct(type_list[i]) + params[var] = ty + + var_list = self.get_parameter_ordering_for_variables(view_obj, params) + + out_count[0] = len(var_list) + result = (core.BNVariable * len(var_list))() + for i, var in enumerate(var_list): + result[i] = var.to_BNVariable() + + result_ptr = ctypes.cast(result, ctypes.c_void_p) + self._pending_variable_lists[result_ptr.value] = (result_ptr.value, result) + + return result_ptr.value + except: + log_error_for_exception( + "Unhandled Python exception in CallingConvention._get_parameter_ordering_for_variables") + out_count[0] = 0 + return None + + def _free_variable_list(self, ctxt, vars, count): + try: + var_list_ptr = ctypes.cast(vars, ctypes.c_void_p) + if var_list_ptr.value is not None: + if var_list_ptr.value not in self._pending_variable_lists: + raise ValueError("freeing variable list that wasn't allocated") + del self._pending_variable_lists[var_list_ptr.value] + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._free_variable_list") + def _get_stack_adjustment_for_locations(self, ctxt, view, ret_value, locations, type_list, param_count): try: if view: @@ -952,6 +1007,28 @@ class CallingConvention: core.BNFreeValueLocationList(locations, count.value) return result + def get_parameter_ordering_for_variables( + self, view: Optional['binaryview.BinaryView'], params: Dict['variable.CoreVariable', 'types.Type'] + ) -> List['variable.CoreVariable']: + return self.get_default_parameter_ordering_for_variables(params) + + def get_default_parameter_ordering_for_variables(self, params: Dict['variable.CoreVariable', 'types.Type']) -> List['variable.CoreVariable']: + vars = (core.BNVariable * len(params))() + types = (ctypes.POINTER(core.BNType) * len(params))() + for (i, (var, ty)) in enumerate(params.items()): + vars[i] = var.to_BNVariable() + types[i] = ty.handle + i += 1 + + count = ctypes.c_ulonglong() + var_list = core.BNGetDefaultParameterOrderingForVariables(self.handle, vars, types, len(params), count) + + result = [] + for i in range(count.value): + result.append(variable.CoreVariable.from_BNVariable(var_list[i])) + core.BNFreeVariableList(var_list) + return result + def get_stack_adjustment_for_locations( self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'], params: List[Tuple['types.ValueLocation', 'types.Type']] @@ -1026,6 +1103,8 @@ class CoreCallingConvention(CallingConvention): self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(handle) self.__dict__["stack_adjusted_on_return"] = core.BNIsStackAdjustedOnReturn(handle) self.__dict__["eligible_for_heuristics"] = core.BNIsEligibleForHeuristics(handle) + self.__dict__["stack_args_naturally_aligned"] = core.BNAreStackArgumentsNaturallyAligned(handle) + self.__dict__["stack_args_pushed_left_to_right"] = core.BNAreStackArgumentsPushedLeftToRight(handle) count = ctypes.c_ulonglong() regs = core.BNGetCallerSavedRegisters(handle, count) @@ -1277,6 +1356,29 @@ class CoreCallingConvention(CallingConvention): core.BNFreeValueLocationList(locations, count.value) return result + def get_parameter_ordering_for_variables( + self, view: Optional['binaryview.BinaryView'], params: Dict['variable.CoreVariable', 'types.Type'] + ) -> List['variable.CoreVariable']: + if view is None: + view_obj = None + else: + view_obj = view.handle + vars = (core.BNVariable * len(params))() + types = (ctypes.POINTER(core.BNType) * len(params))() + for (i, (var, ty)) in enumerate(params.items()): + vars[i] = var.to_BNVariable() + types[i] = ty.handle + i += 1 + + count = ctypes.c_ulonglong() + var_list = core.BNGetParameterOrderingForVariables(self.handle, view_obj, vars, types, len(params), count) + + result = [] + for i in range(count.value): + result.append(variable.CoreVariable.from_BNVariable(var_list[i])) + core.BNFreeVariableList(var_list) + return result + def get_stack_adjustment_for_locations( self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'], params: List[Tuple['types.ValueLocation', 'types.Type']] diff --git a/rust/src/calling_convention.rs b/rust/src/calling_convention.rs index f91e9803..7da7db02 100644 --- a/rust/src/calling_convention.rs +++ b/rust/src/calling_convention.rs @@ -474,6 +474,7 @@ where isArgumentTypeRegisterCompatible: None, isNonRegisterArgumentIndirect: None, areStackArgumentsNaturallyAligned: None, + areStackArgumentsPushedLeftToRight: None, getCallLayout: None, freeCallLayout: None, @@ -481,6 +482,8 @@ where freeValueLocation: None, getParameterLocations: None, freeParameterLocations: None, + getParameterOrderingForVariables: None, + freeVariableList: None, getStackAdjustmentForLocations: None, getRegisterStackAdjustments: None, freeRegisterStackAdjustments: None, -- cgit v1.3.1