Skip to content

Commit e2e420c

Browse files
committed
Allow calling conventions to specify a list of registers that are required to be considered for heuristic calling convention detection
1 parent 4573354 commit e2e420c

File tree

6 files changed

+254
-0
lines changed

6 files changed

+254
-0
lines changed

arch/x86/arch_x86.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3814,6 +3814,11 @@ class X86ThiscallCallingConvention: public X86BaseCallingConvention
38143814
return vector<uint32_t>{ XED_REG_ECX };
38153815
}
38163816

3817+
virtual vector<uint32_t> GetRequiredArgumentRegisters() override
3818+
{
3819+
return vector<uint32_t>{ XED_REG_ECX };
3820+
}
3821+
38173822
virtual bool IsStackAdjustedOnReturn() override
38183823
{
38193824
return true;

binaryninjaapi.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17488,6 +17488,8 @@ namespace BinaryNinja {
1748817488
static uint32_t* GetCalleeSavedRegistersCallback(void* ctxt, size_t* count);
1748917489
static uint32_t* GetIntegerArgumentRegistersCallback(void* ctxt, size_t* count);
1749017490
static uint32_t* GetFloatArgumentRegistersCallback(void* ctxt, size_t* count);
17491+
static uint32_t* GetRequiredArgumentRegistersCallback(void* ctxt, size_t* count);
17492+
static uint32_t* GetRequiredClobberedRegistersCallback(void* ctxt, size_t* count);
1749117493
static void FreeRegisterListCallback(void* ctxt, uint32_t* regs, size_t len);
1749217494

1749317495
static bool AreArgumentRegistersSharedIndexCallback(void* ctxt);
@@ -17520,6 +17522,21 @@ namespace BinaryNinja {
1752017522

1752117523
virtual std::vector<uint32_t> GetIntegerArgumentRegisters();
1752217524
virtual std::vector<uint32_t> GetFloatArgumentRegisters();
17525+
17526+
/*! Gets the set of registers that must be arguments for heuristic calling convention
17527+
detection to consider this calling convention as a valid option.
17528+
17529+
\return The set of registers that must be arguments
17530+
*/
17531+
virtual std::vector<uint32_t> GetRequiredArgumentRegisters();
17532+
17533+
/*! Gets the set of registers that must be clobbered for heuristic calling convention
17534+
detection to consider this calling convention as a valid option.
17535+
17536+
\return The set of registers that must be clobbered
17537+
*/
17538+
virtual std::vector<uint32_t> GetRequiredClobberedRegisters();
17539+
1752317540
virtual bool AreArgumentRegistersSharedIndex();
1752417541
virtual bool AreArgumentRegistersUsedForVarArgs();
1752517542
virtual bool IsStackReservedForArgumentRegisters();
@@ -17552,6 +17569,8 @@ namespace BinaryNinja {
1755217569

1755317570
virtual std::vector<uint32_t> GetIntegerArgumentRegisters() override;
1755417571
virtual std::vector<uint32_t> GetFloatArgumentRegisters() override;
17572+
virtual std::vector<uint32_t> GetRequiredArgumentRegisters() override;
17573+
virtual std::vector<uint32_t> GetRequiredClobberedRegisters() override;
1755517574
virtual bool AreArgumentRegistersSharedIndex() override;
1755617575
virtual bool AreArgumentRegistersUsedForVarArgs() override;
1755717576
virtual bool IsStackReservedForArgumentRegisters() override;

binaryninjacore.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2833,6 +2833,8 @@ extern "C"
28332833
uint32_t* (*getCalleeSavedRegisters)(void* ctxt, size_t* count);
28342834
uint32_t* (*getIntegerArgumentRegisters)(void* ctxt, size_t* count);
28352835
uint32_t* (*getFloatArgumentRegisters)(void* ctxt, size_t* count);
2836+
uint32_t* (*getRequiredArgumentRegisters)(void* ctxt, size_t* count);
2837+
uint32_t* (*getRequiredClobberedRegisters)(void* ctxt, size_t* count);
28362838
void (*freeRegisterList)(void* ctxt, uint32_t* regs, size_t len);
28372839

28382840
bool (*areArgumentRegistersSharedIndex)(void* ctxt);
@@ -7496,6 +7498,8 @@ extern "C"
74967498

74977499
BINARYNINJACOREAPI uint32_t* BNGetIntegerArgumentRegisters(BNCallingConvention* cc, size_t* count);
74987500
BINARYNINJACOREAPI uint32_t* BNGetFloatArgumentRegisters(BNCallingConvention* cc, size_t* count);
7501+
BINARYNINJACOREAPI uint32_t* BNGetRequiredArgumentRegisters(BNCallingConvention* cc, size_t* count);
7502+
BINARYNINJACOREAPI uint32_t* BNGetRequiredClobberedRegisters(BNCallingConvention* cc, size_t* count);
74997503
BINARYNINJACOREAPI bool BNAreArgumentRegistersSharedIndex(BNCallingConvention* cc);
75007504
BINARYNINJACOREAPI bool BNAreArgumentRegistersUsedForVarArgs(BNCallingConvention* cc);
75017505
BINARYNINJACOREAPI bool BNIsStackReservedForArgumentRegisters(BNCallingConvention* cc);

callingconvention.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ CallingConvention::CallingConvention(Architecture* arch, const string& name)
3939
cc.getCalleeSavedRegisters = GetCalleeSavedRegistersCallback;
4040
cc.getIntegerArgumentRegisters = GetIntegerArgumentRegistersCallback;
4141
cc.getFloatArgumentRegisters = GetFloatArgumentRegistersCallback;
42+
cc.getRequiredArgumentRegisters = GetRequiredArgumentRegistersCallback;
43+
cc.getRequiredClobberedRegisters = GetRequiredClobberedRegistersCallback;
4244
cc.freeRegisterList = FreeRegisterListCallback;
4345
cc.areArgumentRegistersSharedIndex = AreArgumentRegistersSharedIndexCallback;
4446
cc.areArgumentRegistersUsedForVarArgs = AreArgumentRegistersUsedForVarArgsCallback;
@@ -119,6 +121,32 @@ uint32_t* CallingConvention::GetFloatArgumentRegistersCallback(void* ctxt, size_
119121
}
120122

121123

124+
uint32_t* CallingConvention::GetRequiredArgumentRegistersCallback(void* ctxt, size_t* count)
125+
{
126+
CallbackRef<CallingConvention> cc(ctxt);
127+
vector<uint32_t> regs = cc->GetRequiredArgumentRegisters();
128+
*count = regs.size();
129+
130+
uint32_t* result = new uint32_t[regs.size()];
131+
for (size_t i = 0; i < regs.size(); i++)
132+
result[i] = regs[i];
133+
return result;
134+
}
135+
136+
137+
uint32_t* CallingConvention::GetRequiredClobberedRegistersCallback(void* ctxt, size_t* count)
138+
{
139+
CallbackRef<CallingConvention> cc(ctxt);
140+
vector<uint32_t> regs = cc->GetRequiredClobberedRegisters();
141+
*count = regs.size();
142+
143+
uint32_t* result = new uint32_t[regs.size()];
144+
for (size_t i = 0; i < regs.size(); i++)
145+
result[i] = regs[i];
146+
return result;
147+
}
148+
149+
122150
void CallingConvention::FreeRegisterListCallback(void*, uint32_t* regs, size_t)
123151
{
124152
delete[] regs;
@@ -284,6 +312,18 @@ vector<uint32_t> CallingConvention::GetFloatArgumentRegisters()
284312
}
285313

286314

315+
vector<uint32_t> CallingConvention::GetRequiredArgumentRegisters()
316+
{
317+
return vector<uint32_t>();
318+
}
319+
320+
321+
vector<uint32_t> CallingConvention::GetRequiredClobberedRegisters()
322+
{
323+
return vector<uint32_t>();
324+
}
325+
326+
287327
bool CallingConvention::AreArgumentRegistersSharedIndex()
288328
{
289329
return false;
@@ -417,6 +457,28 @@ vector<uint32_t> CoreCallingConvention::GetFloatArgumentRegisters()
417457
}
418458

419459

460+
vector<uint32_t> CoreCallingConvention::GetRequiredArgumentRegisters()
461+
{
462+
size_t count;
463+
uint32_t* regs = BNGetRequiredArgumentRegisters(m_object, &count);
464+
vector<uint32_t> result;
465+
result.insert(result.end(), regs, &regs[count]);
466+
BNFreeRegisterList(regs);
467+
return result;
468+
}
469+
470+
471+
vector<uint32_t> CoreCallingConvention::GetRequiredClobberedRegisters()
472+
{
473+
size_t count;
474+
uint32_t* regs = BNGetRequiredClobberedRegisters(m_object, &count);
475+
vector<uint32_t> result;
476+
result.insert(result.end(), regs, &regs[count]);
477+
BNFreeRegisterList(regs);
478+
return result;
479+
}
480+
481+
420482
bool CoreCallingConvention::AreArgumentRegistersSharedIndex()
421483
{
422484
return BNAreArgumentRegistersSharedIndex(m_object);

python/callingconvention.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class CallingConvention:
4040
callee_saved_regs = []
4141
int_arg_regs = []
4242
float_arg_regs = []
43+
required_arg_regs = []
44+
required_clobbered_regs = []
4345
arg_regs_share_index = False
4446
arg_regs_for_varargs = True
4547
stack_reserved_for_arg_regs = False
@@ -70,6 +72,12 @@ def __init__(
7072
self._get_int_arg_regs
7173
)
7274
self._cb.getFloatArgumentRegisters = self._cb.getFloatArgumentRegisters.__class__(self._get_float_arg_regs)
75+
self._cb.getRequiredArgumentRegisters = self._cb.getRequiredArgumentRegisters.__class__(
76+
self._get_required_arg_regs
77+
)
78+
self._cb.getRequiredClobberedRegisters = self._cb.getRequiredClobberedRegisters.__class__(
79+
self._get_required_clobbered_regs
80+
)
7381
self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list)
7482
self._cb.areArgumentRegistersSharedIndex = self._cb.areArgumentRegistersSharedIndex.__class__(
7583
self._arg_regs_share_index
@@ -161,6 +169,26 @@ def __init__(
161169
core.BNFreeRegisterList(regs)
162170
self.__dict__["float_arg_regs"] = result
163171

172+
count = ctypes.c_ulonglong()
173+
regs = core.BNGetRequiredArgumentRegisters(handle, count)
174+
assert regs is not None, "core.BNGetRequiredArgumentRegisters returned None"
175+
result = []
176+
arch = self.arch
177+
for i in range(0, count.value):
178+
result.append(arch.get_reg_name(regs[i]))
179+
core.BNFreeRegisterList(regs)
180+
self.__dict__["required_arg_regs"] = result
181+
182+
count = ctypes.c_ulonglong()
183+
regs = core.BNGetRequiredClobberedRegisters(handle, count)
184+
assert regs is not None, "core.BNGetRequiredClobberedRegisters returned None"
185+
result = []
186+
arch = self.arch
187+
for i in range(0, count.value):
188+
result.append(arch.get_reg_name(regs[i]))
189+
core.BNFreeRegisterList(regs)
190+
self.__dict__["required_clobbered_regs"] = result
191+
164192
reg = core.BNGetIntegerReturnValueRegister(_handle)
165193
if reg == 0xffffffff:
166194
self.__dict__["int_return_reg"] = None
@@ -281,6 +309,36 @@ def _get_float_arg_regs(self, ctxt, count):
281309
count[0] = 0
282310
return None
283311

312+
def _get_required_arg_regs(self, ctxt, count):
313+
try:
314+
regs = self.__class__.required_arg_regs
315+
count[0] = len(regs)
316+
reg_buf = (ctypes.c_uint * len(regs))()
317+
for i in range(0, len(regs)):
318+
reg_buf[i] = self.arch.regs[regs[i]].index
319+
result = ctypes.cast(reg_buf, ctypes.c_void_p)
320+
self._pending_reg_lists[result.value] = (result, reg_buf)
321+
return result.value
322+
except:
323+
log_error_for_exception("Unhandled Python exception in CallingConvention._get_required_arg_regs")
324+
count[0] = 0
325+
return None
326+
327+
def _get_required_clobbered_regs(self, ctxt, count):
328+
try:
329+
regs = self.__class__.required_clobbered_regs
330+
count[0] = len(regs)
331+
reg_buf = (ctypes.c_uint * len(regs))()
332+
for i in range(0, len(regs)):
333+
reg_buf[i] = self.arch.regs[regs[i]].index
334+
result = ctypes.cast(reg_buf, ctypes.c_void_p)
335+
self._pending_reg_lists[result.value] = (result, reg_buf)
336+
return result.value
337+
except:
338+
log_error_for_exception("Unhandled Python exception in CallingConvention._get_required_clobbered_regs")
339+
count[0] = 0
340+
return None
341+
284342
def _free_register_list(self, ctxt, regs, count):
285343
try:
286344
buf = ctypes.cast(regs, ctypes.c_void_p)

0 commit comments

Comments
 (0)