summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2026-01-23 18:36:13 -0500
committerRusty Wagner <rusty.wagner@gmail.com>2026-05-22 16:30:57 -0400
commit217b4a3361cbf62020658f12efb47b8fabb6a24d (patch)
tree59d677957d85851dcd3d380f7e3350b709e08628
parenta4a3de2ad363b1bceb28f106e2cc6d811b3370b5 (diff)
Initial support for Go and Pascal calling conventions
-rw-r--r--arch/x86/arch_x86.cpp277
-rw-r--r--binaryninjaapi.h11
-rw-r--r--binaryninjacore.h17
-rw-r--r--callingconvention.cpp111
-rw-r--r--docs/guide/types/attributes.md12
-rw-r--r--python/callingconvention.py102
-rw-r--r--rust/src/calling_convention.rs3
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<Variable> GetReturnedIndirectReturnValuePointer() override
+ {
+ return std::nullopt;
+ }
+};
+
+
+class X86PascalRegisterCallingConvention : public X86BaseCallingConvention
+{
+public:
+ X86PascalRegisterCallingConvention(Architecture* arch) : X86BaseCallingConvention(arch, "register") {}
+
+ vector<uint32_t> 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<Variable> 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<uint32_t> GetCallerSavedRegisters() override
+ {
+ return vector<uint32_t> { 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<FunctionParameter>& params,
+ const std::optional<set<uint32_t>>& 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<int64_t> varStorage;
+ std::optional<uint64_t> 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<uint32_t> GetCallerSavedRegisters() override
+ {
+ return vector<uint32_t> { 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<FunctionParameter>& params,
+ const std::optional<set<uint32_t>>& 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<int64_t> varStorage;
+ std::optional<uint64_t> 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<FunctionParameter>& params,
@@ -18069,6 +18074,8 @@ namespace BinaryNinja {
virtual std::vector<ValueLocation> GetParameterLocations(BinaryView* view,
const std::optional<ValueLocation>& returnValue, const std::vector<FunctionParameter>& params,
const std::optional<std::set<uint32_t>>& permittedRegs = std::nullopt);
+ virtual std::vector<Variable> GetParameterOrderingForVariables(
+ BinaryView* view, const std::map<Variable, Ref<Type>>& params);
virtual int64_t GetStackAdjustmentForLocations(BinaryView* view,
const std::optional<ValueLocation>& returnValue, const std::vector<ValueLocation>& locations,
const std::vector<Ref<Type>>& types);
@@ -18082,6 +18089,7 @@ namespace BinaryNinja {
std::vector<ValueLocation> GetDefaultParameterLocations(BinaryView* view,
const std::optional<ValueLocation>& returnValue, const std::vector<FunctionParameter>& params,
const std::optional<std::set<uint32_t>>& permittedRegs = std::nullopt);
+ std::vector<Variable> GetDefaultParameterOrderingForVariables(const std::map<Variable, Ref<Type>>& params);
int64_t GetDefaultStackAdjustmentForLocations(const std::optional<ValueLocation>& returnValue,
const std::vector<ValueLocation>& locations, const std::vector<Ref<Type>>& types);
std::map<uint32_t, int32_t> 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<FunctionParameter>& params,
@@ -18136,6 +18145,8 @@ namespace BinaryNinja {
virtual std::vector<ValueLocation> GetParameterLocations(BinaryView* view,
const std::optional<ValueLocation>& returnValue, const std::vector<FunctionParameter>& params,
const std::optional<std::set<uint32_t>>& permittedRegs = std::nullopt) override;
+ virtual std::vector<Variable> GetParameterOrderingForVariables(
+ BinaryView* view, const std::map<Variable, Ref<Type>>& params) override;
virtual int64_t GetStackAdjustmentForLocations(BinaryView* view,
const std::optional<ValueLocation>& returnValue, const std::vector<ValueLocation>& locations,
const std::vector<Ref<Type>>& 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<CallingConvention> 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<CallingConvention> cc(ctxt);
+ Ref<BinaryView> viewObj;
+ if (view)
+ viewObj = new BinaryView(BNNewViewReference(view));
+ map<Variable, Ref<Type>> 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<FunctionParameter>& params, const optional<set<uint32_t>>& permittedRegs)
{
@@ -771,6 +814,13 @@ vector<ValueLocation> CallingConvention::GetParameterLocations(BinaryView* view,
}
+std::vector<Variable> CallingConvention::GetParameterOrderingForVariables(
+ BinaryView*, const std::map<Variable, Ref<Type>>& params)
+{
+ return GetDefaultParameterOrderingForVariables(params);
+}
+
+
int64_t CallingConvention::GetStackAdjustmentForLocations(BinaryView*, const std::optional<ValueLocation>& returnValue,
const std::vector<ValueLocation>& locations, const std::vector<Ref<Type>>& types)
{
@@ -881,6 +931,33 @@ vector<ValueLocation> CallingConvention::GetDefaultParameterLocations(BinaryView
}
+std::vector<Variable> CallingConvention::GetDefaultParameterOrderingForVariables(
+ const std::map<Variable, Ref<Type>>& 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<Variable> 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<ValueLocation>& returnValue,
const std::vector<ValueLocation>& locations, const std::vector<Ref<Type>>& 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<FunctionParameter>& params, const optional<set<uint32_t>>& permittedRegs)
{
@@ -1241,6 +1324,34 @@ vector<ValueLocation> CoreCallingConvention::GetParameterLocations(BinaryView* v
}
+std::vector<Variable> CoreCallingConvention::GetParameterOrderingForVariables(
+ BinaryView* view, const std::map<Variable, Ref<Type>>& 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<Variable> 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<ValueLocation>& returnValue, const std::vector<ValueLocation>& locations,
const std::vector<Ref<Type>>& 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,