Skip to content

Commit 8a7c156

Browse files
committed
Initial support for Go and Pascal calling conventions
1 parent 2166a8c commit 8a7c156

7 files changed

Lines changed: 529 additions & 4 deletions

File tree

arch/x86/arch_x86.cpp

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3971,6 +3971,170 @@ class X86LinuxSystemCallConvention: public CallingConvention
39713971
};
39723972

39733973

3974+
class X86PascalCallingConvention : public X86BaseCallingConvention
3975+
{
3976+
public:
3977+
X86PascalCallingConvention(Architecture* arch) : X86BaseCallingConvention(arch, "pascal") {}
3978+
3979+
bool IsNonRegisterArgumentIndirect(BinaryView*, Type* type) override
3980+
{
3981+
return type && !type->IsFloat() && type->GetWidth() > 4;
3982+
}
3983+
3984+
bool IsStackAdjustedOnReturn() override
3985+
{
3986+
return true;
3987+
}
3988+
3989+
bool AreStackArgumentsPushedLeftToRight() override
3990+
{
3991+
return true;
3992+
}
3993+
3994+
Variable GetIndirectReturnValueLocation() override
3995+
{
3996+
// Return value pointer is always at the top of the stack (effectively the last parameter
3997+
// in a left-to-right convention)
3998+
return Variable::StackOffset(4);
3999+
}
4000+
4001+
std::optional<Variable> GetReturnedIndirectReturnValuePointer() override
4002+
{
4003+
return std::nullopt;
4004+
}
4005+
};
4006+
4007+
4008+
class X86PascalRegisterCallingConvention : public X86BaseCallingConvention
4009+
{
4010+
public:
4011+
X86PascalRegisterCallingConvention(Architecture* arch) : X86BaseCallingConvention(arch, "register") {}
4012+
4013+
vector<uint32_t> GetIntegerArgumentRegisters() override
4014+
{
4015+
return { XED_REG_EAX, XED_REG_EDX, XED_REG_ECX };
4016+
}
4017+
4018+
bool IsNonRegisterArgumentIndirect(BinaryView*, Type* type) override
4019+
{
4020+
return type && !type->IsFloat() && type->GetWidth() > 4;
4021+
}
4022+
4023+
bool AreStackArgumentsPushedLeftToRight() override
4024+
{
4025+
return true;
4026+
}
4027+
4028+
std::optional<Variable> GetReturnedIndirectReturnValuePointer() override
4029+
{
4030+
return std::nullopt;
4031+
}
4032+
};
4033+
4034+
4035+
class X86GoStackCallingConvention: public CallingConvention
4036+
{
4037+
public:
4038+
X86GoStackCallingConvention(Architecture* arch): CallingConvention(arch, "go-stack")
4039+
{
4040+
}
4041+
4042+
bool IsEligibleForHeuristics() override
4043+
{
4044+
// This convention cannot be detected by heuristics at this time and will cause issues
4045+
// with non-Go code.
4046+
return false;
4047+
}
4048+
4049+
uint32_t GetIntegerReturnValueRegister() override
4050+
{
4051+
return BN_INVALID_REGISTER;
4052+
}
4053+
4054+
vector<uint32_t> GetCallerSavedRegisters() override
4055+
{
4056+
return vector<uint32_t> { XED_REG_EAX, XED_REG_ECX, XED_REG_EDX, XED_REG_EBX, XED_REG_EBP };
4057+
}
4058+
4059+
RegisterValue GetIncomingFlagValue(uint32_t flag, Function*) override
4060+
{
4061+
RegisterValue result;
4062+
if (flag == IL_FLAG_D)
4063+
{
4064+
result.state = ConstantValue;
4065+
result.value = 0;
4066+
}
4067+
return result;
4068+
}
4069+
4070+
ValueLocation GetReturnValueLocation(BinaryView*, const ReturnValue&) override
4071+
{
4072+
// It is not possible for this API to determine the return value location on the stack at
4073+
// this point, return an invalid location and fall back to GetCallLayout.
4074+
return ValueLocation();
4075+
}
4076+
4077+
CallLayout GetCallLayout(BinaryView* view, const ReturnValue& returnValue, const vector<FunctionParameter>& params,
4078+
const std::optional<set<uint32_t>>& permittedRegs) override
4079+
{
4080+
CallLayout result;
4081+
result.parameters = GetParameterLocations(view, result.returnValue, params, permittedRegs);
4082+
4083+
if (returnValue.type.GetValue() && returnValue.type->GetClass() != VoidTypeClass)
4084+
{
4085+
if (returnValue.defaultLocation)
4086+
{
4087+
int64_t stackOffset = 4;
4088+
size_t i = 0;
4089+
for (auto it = result.parameters.begin(); it != result.parameters.end(); ++i, ++it)
4090+
{
4091+
std::optional<int64_t> varStorage;
4092+
std::optional<uint64_t> varSize;
4093+
for (auto& component: it->components)
4094+
{
4095+
if (component.variable.type != StackVariableSourceType)
4096+
continue;
4097+
if (!varStorage.has_value() || component.variable.storage > varStorage.value())
4098+
{
4099+
varStorage = component.variable.storage;
4100+
if (!it->indirect)
4101+
varSize = component.size;
4102+
}
4103+
}
4104+
4105+
if (!varStorage.has_value() || varStorage.value() < stackOffset)
4106+
continue;
4107+
if (it->indirect)
4108+
varSize = 4;
4109+
4110+
size_t width = 4;
4111+
if (varSize.has_value())
4112+
width = varSize.value();
4113+
else if (i < params.size() && params[i].type.GetValue())
4114+
width = params[i].type->GetWidth();
4115+
4116+
if (width < 4)
4117+
width = 4;
4118+
else if ((width % 4) != 0)
4119+
width += 4 - (width % 4);
4120+
4121+
stackOffset = varStorage.value() + width;
4122+
}
4123+
4124+
result.returnValue = Variable::StackOffset(stackOffset);
4125+
}
4126+
else
4127+
{
4128+
result.returnValue = returnValue.location.GetValue();
4129+
}
4130+
}
4131+
4132+
result.registerStackAdjustments = GetRegisterStackAdjustments(view, result.returnValue, result.parameters);
4133+
return result;
4134+
}
4135+
};
4136+
4137+
39744138
class X64BaseCallingConvention: public CallingConvention
39754139
{
39764140
public:
@@ -4145,6 +4309,111 @@ class X64LinuxSystemCallConvention: public CallingConvention
41454309
};
41464310

41474311

4312+
class X64GoStackCallingConvention: public CallingConvention
4313+
{
4314+
public:
4315+
X64GoStackCallingConvention(Architecture* arch): CallingConvention(arch, "go-stack")
4316+
{
4317+
}
4318+
4319+
bool IsEligibleForHeuristics() override
4320+
{
4321+
// This convention cannot be detected by heuristics at this time and will cause issues
4322+
// with non-Go code.
4323+
return false;
4324+
}
4325+
4326+
uint32_t GetIntegerReturnValueRegister() override
4327+
{
4328+
return BN_INVALID_REGISTER;
4329+
}
4330+
4331+
vector<uint32_t> GetCallerSavedRegisters() override
4332+
{
4333+
return vector<uint32_t> { XED_REG_RAX, XED_REG_RCX, XED_REG_RDX, XED_REG_RBX, XED_REG_RBP,
4334+
XED_REG_R8, XED_REG_R9, XED_REG_R10, XED_REG_R11, XED_REG_R12, XED_REG_R13, XED_REG_R14,
4335+
XED_REG_R15 };
4336+
}
4337+
4338+
RegisterValue GetIncomingFlagValue(uint32_t flag, Function*) override
4339+
{
4340+
RegisterValue result;
4341+
if (flag == IL_FLAG_D)
4342+
{
4343+
result.state = ConstantValue;
4344+
result.value = 0;
4345+
}
4346+
return result;
4347+
}
4348+
4349+
ValueLocation GetReturnValueLocation(BinaryView*, const ReturnValue&) override
4350+
{
4351+
// It is not possible for this API to determine the return value location on the stack at
4352+
// this point, return an invalid location and fall back to GetCallLayout.
4353+
return ValueLocation();
4354+
}
4355+
4356+
CallLayout GetCallLayout(BinaryView* view, const ReturnValue& returnValue, const vector<FunctionParameter>& params,
4357+
const std::optional<set<uint32_t>>& permittedRegs) override
4358+
{
4359+
CallLayout result;
4360+
result.parameters = GetParameterLocations(view, result.returnValue, params, permittedRegs);
4361+
4362+
if (returnValue.type.GetValue() && returnValue.type->GetClass() != VoidTypeClass)
4363+
{
4364+
if (returnValue.defaultLocation)
4365+
{
4366+
int64_t stackOffset = 8;
4367+
size_t i = 0;
4368+
for (auto it = result.parameters.begin(); it != result.parameters.end(); ++i, ++it)
4369+
{
4370+
std::optional<int64_t> varStorage;
4371+
std::optional<uint64_t> varSize;
4372+
for (auto& component: it->components)
4373+
{
4374+
if (component.variable.type != StackVariableSourceType)
4375+
continue;
4376+
if (!varStorage.has_value() || component.variable.storage > varStorage.value())
4377+
{
4378+
varStorage = component.variable.storage;
4379+
if (!it->indirect)
4380+
varSize = component.size;
4381+
}
4382+
}
4383+
4384+
if (!varStorage.has_value() || varStorage.value() < stackOffset)
4385+
continue;
4386+
if (it->indirect)
4387+
varSize = 8;
4388+
4389+
size_t width = 8;
4390+
if (varSize.has_value())
4391+
width = varSize.value();
4392+
else if (i < params.size() && params[i].type.GetValue())
4393+
width = params[i].type->GetWidth();
4394+
4395+
if (width < 8)
4396+
width = 8;
4397+
else if ((width % 8) != 0)
4398+
width += 8 - (width % 8);
4399+
4400+
stackOffset = varStorage.value() + width;
4401+
}
4402+
4403+
result.returnValue = Variable::StackOffset(stackOffset);
4404+
}
4405+
else
4406+
{
4407+
result.returnValue = returnValue.location.GetValue();
4408+
}
4409+
}
4410+
4411+
result.registerStackAdjustments = GetRegisterStackAdjustments(view, result.returnValue, result.parameters);
4412+
return result;
4413+
}
4414+
};
4415+
4416+
41484417
class x86MachoRelocationHandler: public RelocationHandler
41494418
{
41504419
public:
@@ -5052,6 +5321,12 @@ extern "C"
50525321
x86->RegisterCallingConvention(conv);
50535322
conv = new X86LinuxSystemCallConvention(x86);
50545323
x86->RegisterCallingConvention(conv);
5324+
conv = new X86PascalCallingConvention(x86);
5325+
x86->RegisterCallingConvention(conv);
5326+
conv = new X86PascalRegisterCallingConvention(x86);
5327+
x86->RegisterCallingConvention(conv);
5328+
conv = new X86GoStackCallingConvention(x86);
5329+
x86->RegisterCallingConvention(conv);
50555330

50565331
x86->RegisterRelocationHandler("Mach-O", new x86MachoRelocationHandler());
50575332
x86->RegisterRelocationHandler("KCView", new x86MachoRelocationHandler());
@@ -5069,6 +5344,8 @@ extern "C"
50695344
x64->RegisterCallingConvention(conv);
50705345
conv = new X64LinuxSystemCallConvention(x64);
50715346
x64->RegisterCallingConvention(conv);
5347+
conv = new X64GoStackCallingConvention(x64);
5348+
x64->RegisterCallingConvention(conv);
50725349

50735350
x64->RegisterRelocationHandler("Mach-O", new x64MachoRelocationHandler());
50745351
x64->RegisterRelocationHandler("KCView", new x64MachoRelocationHandler());

binaryninjaapi.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17946,6 +17946,7 @@ namespace BinaryNinja {
1794617946
static bool IsArgumentTypeRegisterCompatibleCallback(void* ctxt, BNBinaryView* view, BNType* type);
1794717947
static bool IsNonRegisterArgumentIndirectCallback(void* ctxt, BNBinaryView* view, BNType* type);
1794817948
static bool AreStackArgumentsNaturallyAlignedCallback(void* ctxt);
17949+
static bool AreStackArgumentsPushedLeftToRightCallback(void* ctxt);
1794917950

1795017951
static void GetCallLayoutCallback(void* ctxt, BNBinaryView* view, BNReturnValue* returnValue,
1795117952
BNFunctionParameter* params, size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs,
@@ -17958,6 +17959,9 @@ namespace BinaryNinja {
1795817959
BNValueLocation* returnValue, BNFunctionParameter* params, size_t paramCount, bool hasPermittedRegs,
1795917960
uint32_t* permittedRegs, size_t permittedRegCount, size_t* outLocationCount);
1796017961
static void FreeParameterLocationsCallback(void* ctxt, BNValueLocation* locations, size_t count);
17962+
static BNVariable* GetParameterOrderingForVariablesCallback(
17963+
void* ctxt, BNBinaryView* view, BNVariable* vars, BNType** types, size_t paramCount, size_t* outCount);
17964+
static void FreeVariableListCallback(void* ctxt, BNVariable* vars, size_t count);
1796117965
static int64_t GetStackAdjustmentForLocationsCallback(void* ctxt, BNBinaryView* view,
1796217966
BNValueLocation* returnValue, BNValueLocation* locations, BNType** types, size_t paramCount);
1796317967
static size_t GetRegisterStackAdjustmentsCallback(void* ctxt, BNBinaryView* view, BNValueLocation* returnValue,
@@ -18016,6 +18020,7 @@ namespace BinaryNinja {
1801618020
bool DefaultIsArgumentTypeRegisterCompatible(Type* type);
1801718021
virtual bool IsNonRegisterArgumentIndirect(BinaryView* view, Type* type);
1801818022
virtual bool AreStackArgumentsNaturallyAligned();
18023+
virtual bool AreStackArgumentsPushedLeftToRight();
1801918024

1802018025
virtual CallLayout GetCallLayout(BinaryView* view, const ReturnValue& returnValue,
1802118026
const std::vector<FunctionParameter>& params,
@@ -18024,6 +18029,8 @@ namespace BinaryNinja {
1802418029
virtual std::vector<ValueLocation> GetParameterLocations(BinaryView* view,
1802518030
const std::optional<ValueLocation>& returnValue, const std::vector<FunctionParameter>& params,
1802618031
const std::optional<std::set<uint32_t>>& permittedRegs = std::nullopt);
18032+
virtual std::vector<Variable> GetParameterOrderingForVariables(
18033+
BinaryView* view, const std::map<Variable, Ref<Type>>& params);
1802718034
virtual int64_t GetStackAdjustmentForLocations(BinaryView* view,
1802818035
const std::optional<ValueLocation>& returnValue, const std::vector<ValueLocation>& locations,
1802918036
const std::vector<Ref<Type>>& types);
@@ -18037,6 +18044,7 @@ namespace BinaryNinja {
1803718044
std::vector<ValueLocation> GetDefaultParameterLocations(BinaryView* view,
1803818045
const std::optional<ValueLocation>& returnValue, const std::vector<FunctionParameter>& params,
1803918046
const std::optional<std::set<uint32_t>>& permittedRegs = std::nullopt);
18047+
std::vector<Variable> GetDefaultParameterOrderingForVariables(const std::map<Variable, Ref<Type>>& params);
1804018048
int64_t GetDefaultStackAdjustmentForLocations(const std::optional<ValueLocation>& returnValue,
1804118049
const std::vector<ValueLocation>& locations, const std::vector<Ref<Type>>& types);
1804218050
std::map<uint32_t, int32_t> GetDefaultRegisterStackAdjustments(
@@ -18083,6 +18091,7 @@ namespace BinaryNinja {
1808318091
virtual bool IsArgumentTypeRegisterCompatible(BinaryView* view, Type* type) override;
1808418092
virtual bool IsNonRegisterArgumentIndirect(BinaryView* view, Type* type) override;
1808518093
virtual bool AreStackArgumentsNaturallyAligned() override;
18094+
virtual bool AreStackArgumentsPushedLeftToRight() override;
1808618095

1808718096
virtual CallLayout GetCallLayout(BinaryView* view, const ReturnValue& returnValue,
1808818097
const std::vector<FunctionParameter>& params,
@@ -18091,6 +18100,8 @@ namespace BinaryNinja {
1809118100
virtual std::vector<ValueLocation> GetParameterLocations(BinaryView* view,
1809218101
const std::optional<ValueLocation>& returnValue, const std::vector<FunctionParameter>& params,
1809318102
const std::optional<std::set<uint32_t>>& permittedRegs = std::nullopt) override;
18103+
virtual std::vector<Variable> GetParameterOrderingForVariables(
18104+
BinaryView* view, const std::map<Variable, Ref<Type>>& params) override;
1809418105
virtual int64_t GetStackAdjustmentForLocations(BinaryView* view,
1809518106
const std::optional<ValueLocation>& returnValue, const std::vector<ValueLocation>& locations,
1809618107
const std::vector<Ref<Type>>& types) override;

0 commit comments

Comments
 (0)