summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--binaryninjaapi.h335
-rw-r--r--docs/dev/archplatform-platform.md10
-rw-r--r--docs/dev/bnil-hlil.md2
-rw-r--r--docs/dev/bnil-mlil.md15
-rw-r--r--docs/guide/types/attributes.md61
-rw-r--r--python/callingconvention.py325
6 files changed, 745 insertions, 3 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 70c6552f..d81ddb11 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -18014,13 +18014,43 @@ namespace BinaryNinja {
static void FreeRegisterStackAdjustmentsCallback(void* ctxt, uint32_t* regs, int32_t* adjust, size_t count);
public:
+ /*! Get the architecture this calling convention applies to
+
+ \return The architecture this calling convention applies to
+ */
Ref<Architecture> GetArchitecture() const;
+
+ /*! Get the name of this calling convention
+
+ \return The name of this calling convention
+ */
std::string GetName() const;
+ /*! Gets the list of registers that are not preserved across a call (caller-saved /
+ volatile registers).
+
+ \return The list of caller-saved register indices
+ */
virtual std::vector<uint32_t> GetCallerSavedRegisters();
+
+ /*! Gets the list of registers that a callee must preserve across a call (callee-saved /
+ non-volatile registers).
+
+ \return The list of callee-saved register indices
+ */
virtual std::vector<uint32_t> GetCalleeSavedRegisters();
+ /*! Gets the registers used to pass integer and pointer arguments, in the order they are
+ used.
+
+ \return The ordered list of integer argument register indices
+ */
virtual std::vector<uint32_t> GetIntegerArgumentRegisters();
+
+ /*! Gets the registers used to pass floating point arguments, in the order they are used.
+
+ \return The ordered list of floating point argument register indices
+ */
virtual std::vector<uint32_t> GetFloatArgumentRegisters();
/*! Gets the set of registers that must be arguments for heuristic calling convention
@@ -18037,61 +18067,366 @@ namespace BinaryNinja {
*/
virtual std::vector<uint32_t> GetRequiredClobberedRegisters();
+ /*! Whether the integer and floating point argument registers share a single argument
+ index.
+
+ When true, the Nth argument consumes the Nth slot of both the integer and float
+ register lists regardless of its type. When false, integer and float arguments are
+ assigned from their respective register lists independently.
+
+ \return Whether argument registers share a single index
+ */
virtual bool AreArgumentRegistersSharedIndex();
+
+ /*! Whether argument registers are used to pass variadic arguments.
+
+ \return Whether argument registers are used for variadic arguments
+ */
virtual bool AreArgumentRegistersUsedForVarArgs();
+
+ /*! Whether stack space is reserved by the caller for the register arguments (for example,
+ the shadow/home space used by the Windows x64 calling convention).
+
+ \return Whether stack space is reserved for argument registers
+ */
virtual bool IsStackReservedForArgumentRegisters();
+
+ /*! Whether the callee adjusts the stack to remove the arguments before returning (as in
+ stdcall), rather than leaving the caller to clean up the stack (as in cdecl).
+
+ \return Whether the stack is adjusted by the callee on return
+ */
virtual bool IsStackAdjustedOnReturn();
+
+ /*! Whether this calling convention may be selected by heuristic calling convention
+ detection.
+
+ \return Whether this calling convention is eligible for heuristics
+ */
virtual bool IsEligibleForHeuristics();
+ /*! Gets the register that holds the integer return value.
+
+ \return The integer return value register index
+ */
virtual uint32_t GetIntegerReturnValueRegister() = 0;
+
+ /*! Gets the register that holds the high part of an integer return value that is too
+ large to fit in a single register.
+
+ \return The high integer return value register index, or BN_INVALID_REGISTER if there is none
+ */
virtual uint32_t GetHighIntegerReturnValueRegister();
+
+ /*! Gets the register that holds the floating point return value.
+
+ \return The floating point return value register index, or BN_INVALID_REGISTER if there is none
+ */
virtual uint32_t GetFloatReturnValueRegister();
+
+ /*! Gets the register that holds the global pointer, if the calling convention defines one.
+
+ \return The global pointer register index, or BN_INVALID_REGISTER if there is none
+ */
virtual uint32_t GetGlobalPointerRegister();
+ /*! Gets the registers that are implicitly given a known value on function entry by this
+ calling convention.
+
+ \return The list of implicitly defined register indices
+ \see GetIncomingRegisterValue
+ */
virtual std::vector<uint32_t> GetImplicitlyDefinedRegisters();
+
+ /*! Gets the known value of a register on entry to a function.
+
+ \param reg Register index
+ \param func Function being analyzed
+ \return The incoming value of the register
+ */
virtual RegisterValue GetIncomingRegisterValue(uint32_t reg, Function* func);
+
+ /*! Gets the known value of a flag on entry to a function.
+
+ \param flag Flag index
+ \param func Function being analyzed
+ \return The incoming value of the flag
+ */
virtual RegisterValue GetIncomingFlagValue(uint32_t flag, Function* func);
+ /*! Gets the incoming variable that corresponds to the given parameter variable. This is
+ the inverse of GetParameterVariableForIncomingVariable.
+
+ \param var Parameter variable
+ \param func Function being analyzed
+ \return The incoming variable corresponding to the parameter variable
+ \see GetParameterVariableForIncomingVariable
+ */
virtual Variable GetIncomingVariableForParameterVariable(const Variable& var, Function* func);
+
+ /*! Gets the parameter variable that corresponds to the given incoming variable. This is
+ the inverse of GetIncomingVariableForParameterVariable.
+
+ \param var Incoming variable
+ \param func Function being analyzed
+ \return The parameter variable corresponding to the incoming variable
+ \see GetIncomingVariableForParameterVariable
+ */
virtual Variable GetParameterVariableForIncomingVariable(const Variable& var, Function* func);
+ /*! Whether a value of the given type can be returned in registers, as opposed to being
+ returned indirectly through memory.
+
+ \param view BinaryView providing type information
+ \param type Return type to check
+ \return Whether the return type is register compatible
+ \see GetIndirectReturnValueLocation
+ */
virtual bool IsReturnTypeRegisterCompatible(BinaryView* view, Type* type);
+
+ /*! Default implementation of IsReturnTypeRegisterCompatible. The default implementation allows
+ register returns for types that fit in a single register, have a size equal to two registers
+ when GetHighIntegerReturnValueRegister is a valid register, or are a floating point type when
+ GetFloatReturnValueRegister is a valid register.
+
+ \param type Return type to check
+ \return Whether the return type is register compatible
+ */
bool DefaultIsReturnTypeRegisterCompatible(Type* type);
+
+ /*! Gets the location used to pass the hidden pointer argument for return values that are
+ returned indirectly through memory.
+
+ \return The location of the indirect return value pointer
+ \see IsReturnTypeRegisterCompatible
+ */
virtual Variable GetIndirectReturnValueLocation();
+
+ /*! Default implementation of GetIndirectReturnValueLocation. The default location is the first
+ integer argument register, or the first stack slot if there are no integer argument registers.
+
+ \return The location of the indirect return value pointer
+ */
Variable GetDefaultIndirectReturnValueLocation();
+
+ /*! Gets the location in which the hidden indirect return value pointer is returned to the
+ caller, for calling conventions that return it.
+
+ \return The location the indirect return value pointer is returned in, or std::nullopt if it is not returned
+ */
virtual std::optional<Variable> GetReturnedIndirectReturnValuePointer();
+ /*! Whether a value of the given type can be passed as an argument in registers.
+
+ \param view BinaryView providing type information
+ \param type Argument type to check
+ \return Whether the argument type is register compatible
+ */
virtual bool IsArgumentTypeRegisterCompatible(BinaryView* view, Type* type);
+
+ /*! Default implementation of IsArgumentTypeRegisterCompatible. The default implementation allows
+ register arguments for types that fit in a single register, or are a floating point type when
+ GetFloatArgumentRegisters has valid registers.
+
+ \param type Argument type to check
+ \return Whether the argument type is register compatible
+ */
bool DefaultIsArgumentTypeRegisterCompatible(Type* type);
+
+ /*! Whether an argument that cannot be passed in registers is passed indirectly by pointer
+ as opposed to being passed directly on the stack.
+
+ \param view BinaryView providing type information
+ \param type Argument type to check
+ \return Whether the non-register argument is passed indirectly by pointer
+ */
virtual bool IsNonRegisterArgumentIndirect(BinaryView* view, Type* type);
+
+ /*! Whether arguments passed on the stack are aligned to their natural alignment. If false,
+ arguments are aligned to the address size.
+
+ \return Whether stack arguments are naturally aligned
+ */
virtual bool AreStackArgumentsNaturallyAligned();
+
+ /*! Whether arguments passed on the stack are pushed left-to-right, as opposed to the more
+ common right-to-left order.
+
+ \return Whether stack arguments are pushed left-to-right
+ */
virtual bool AreStackArgumentsPushedLeftToRight();
+ /*! Computes the complete call layout (parameter locations, return value location, and
+ stack adjustments) for a call with the given return value and parameters. It is
+ recommended to only override this method if the calling convention behavior cannot
+ be modeled with GetReturnValueLocation and/or GetParameterLocations.
+
+ The default implementation calls GetDefaultCallLayout.
+
+ When calling this function to query the layout of a function, the return value and parameters
+ should have their named type references dereferenced before passing them to this function.
+ Calling the functions BinaryView::DerefReturnValueNamedTypeRefs and
+ BinaryView::DerefParameterNamedTypeRefs will perform this dereferencing.
+
+ \param view BinaryView providing type information
+ \param returnValue Return value of the call
+ \param params Parameters of the call
+ \param permittedRegs Optional set of register indices that argument passing is
+ restricted to; if not provided, the calling convention's default registers are used
+ \return The computed call layout
+ */
virtual CallLayout GetCallLayout(BinaryView* view, const ReturnValue& returnValue,
const std::vector<FunctionParameter>& params,
const std::optional<std::set<uint32_t>>& permittedRegs = std::nullopt);
+
+ /*! Computes the location of the return value for the given return value type and location structure.
+
+ The default implementation calls GetDefaultReturnValueLocation.
+
+ \param view BinaryView providing type information
+ \param returnValue Return value to compute the location for
+ \return The location of the return value
+ */
virtual ValueLocation GetReturnValueLocation(BinaryView* view, const ReturnValue& returnValue);
+
+ /*! Computes the locations of the parameters for a call with the given return value and
+ parameters.
+
+ The default implementation calls GetDefaultParameterLocations.
+
+ \param view BinaryView providing type information
+ \param returnValue Optional location of the return value, which may affect parameter
+ placement (for example, when an indirect return pointer consumes an argument
+ register)
+ \param params Parameters of the call
+ \param permittedRegs Optional set of register indices that argument passing is
+ restricted to; if not provided, the calling convention's default registers are used
+ \return The locations of the parameters, in order
+ */
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);
+
+ /*! Computes the order in which the given parameter variables are passed. Used by the heuristic
+ calling convention detection to create a function type from a list of parameter variables.
+
+ The default implementation calls GetDefaultParameterOrderingForVariables.
+
+ \param view BinaryView providing type information
+ \param params Map of parameter variables to their types
+ \return The parameter variables in the order they are passed
+ */
virtual std::vector<Variable> GetParameterOrderingForVariables(
BinaryView* view, const std::map<Variable, Ref<Type>>& params);
+
+ /*! Computes the stack adjustment applied on return for a call with the given return value
+ and parameter locations.
+
+ The default implementation calls GetDefaultStackAdjustmentForLocations.
+
+ \param view BinaryView providing type information
+ \param returnValue Optional location of the return value
+ \param locations Locations of the parameters
+ \param types Types of the parameters, corresponding to \p locations
+ \return The stack adjustment in bytes
+ \see IsStackAdjustedOnReturn
+ */
virtual int64_t GetStackAdjustmentForLocations(BinaryView* view,
const std::optional<ValueLocation>& returnValue, const std::vector<ValueLocation>& locations,
const std::vector<Ref<Type>>& types);
+
+ /*! Computes the per-register-stack adjustments (for architectures with register stacks,
+ such as the x87 floating point stack) for a call with the given return value and
+ parameter locations.
+
+ The default implementation calls GetDefaultRegisterStackAdjustments.
+
+ \param view BinaryView providing type information
+ \param returnValue Optional location of the return value
+ \param params Locations of the parameters
+ \return A map from register stack index to its adjustment
+ */
virtual std::map<uint32_t, int32_t> GetRegisterStackAdjustments(BinaryView* view,
const std::optional<ValueLocation>& returnValue, const std::vector<ValueLocation>& params);
+ /*! Default implementation of GetCallLayout. The default implementation uses GetReturnValueLocation,
+ GetParameterLocations, GetStackAdjustmentForLocations, and GetRegisterStackAdjustments to
+ compute the layout.
+
+ \param view BinaryView providing type information
+ \param returnValue Return value of the call
+ \param params Parameters of the call
+ \param permittedRegs Optional set of register indices that argument passing is
+ restricted to; if not provided, the calling convention's default registers are used
+ \return The computed call layout
+ */
CallLayout GetDefaultCallLayout(BinaryView* view, const ReturnValue& returnValue,
const std::vector<FunctionParameter>& params,
const std::optional<std::set<uint32_t>>& permittedRegs = std::nullopt);
+
+ /*! Default implementation of GetReturnValueLocation. The default implementation checks
+ IsReturnTypeRegisterCompatible and places the return value in registers if it can,
+ or uses an indirect return by pointer if not. If an indirect return is required, then
+ GetIndirectReturnValueLocation and GetReturnedIndirectReturnValuePointer are used
+ to provide the location of the indirect return value.
+
+ \param view BinaryView providing type information
+ \param returnValue Return value to compute the location for
+ \return The location of the return value
+ */
ValueLocation GetDefaultReturnValueLocation(BinaryView* view, const ReturnValue& returnValue);
+
+ /*! Default implementation of GetParameterLocations. The default implementation uses
+ GetIntegerArgumentRegisters, GetFloatArgumentRegisters, AreArgumentRegistersSharedIndex,
+ IsStackReservedForArgumentRegisters, IsArgumentTypeRegisterCompatible, IsNonRegisterArgumentIndirect,
+ AreStackArgumentsNaturallyAligned, and AreStackArgumentsPushedLeftToRight to
+ compute the parameter layout.
+
+ This function is usually sufficient unless the calling convention has unusual parameter
+ passing behavior. Most calling conventions can be defined per-argument using the methods
+ listed above.
+
+ \param view BinaryView providing type information
+ \param returnValue Optional location of the return value
+ \param params Parameters of the call
+ \param permittedRegs Optional set of register indices that argument passing is
+ restricted to; if not provided, the calling convention's default registers are used
+ \return The locations of the parameters, in order
+ */
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);
+
+ /*! Default implementation of GetParameterOrderingForVariables. The default implementation first checks
+ AreArgumentRegistersSharedIndex to see if the parameter ordering is well defined. If the arguments
+ do not share an index, it places all integer arguments before the floating point arguments.
+ Arguments that are not passed in a normal location are placed last.
+
+ \param params Map of parameter variables to their types
+ \return The parameter variables in the order they are passed
+ */
std::vector<Variable> GetDefaultParameterOrderingForVariables(const std::map<Variable, Ref<Type>>& params);
+
+ /*! Default implementation of GetStackAdjustmentForLocations. The default implementation first checks
+ IsStackAdjustedOnReturn, and returns zero if that returns false. Otherwise, it checks the stack
+ parameter locations and AreStackArgumentsNaturallyAligned to compute the stack adjustment necessary
+ to cover all parameters.
+
+ \param returnValue Optional location of the return value
+ \param locations Locations of the parameters
+ \param types Types of the parameters, corresponding to \p locations
+ \return The stack adjustment in bytes
+ */
int64_t GetDefaultStackAdjustmentForLocations(const std::optional<ValueLocation>& returnValue,
const std::vector<ValueLocation>& locations, const std::vector<Ref<Type>>& types);
+
+ /*! Default implementation of GetRegisterStackAdjustments. The default implementation compares the
+ register stack slots used by the parameters and the return value to compute the adjustments.
+
+ \param returnValue Optional location of the return value
+ \param params Locations of the parameters
+ \return A map from register stack index to its adjustment
+ */
std::map<uint32_t, int32_t> GetDefaultRegisterStackAdjustments(
const std::optional<ValueLocation>& returnValue, const std::vector<ValueLocation>& params);
};
diff --git a/docs/dev/archplatform-platform.md b/docs/dev/archplatform-platform.md
index 4a5a8086..3d078624 100644
--- a/docs/dev/archplatform-platform.md
+++ b/docs/dev/archplatform-platform.md
@@ -55,6 +55,16 @@ Other fields that Quark did not need, but you can specify:
* `implicitly_defined_regs` - Certain calling conventions pass registers to calls which are not included in type signatures (such as how MIPS on Linux sets `$t9` to the address of the called function, but this should not clutter up the type signature).
* `required_arg_regs` - If specified, heuristic calling convention detection will only consider this calling convention if all the registers specified here are used before they are defined.
* `required_clobbered_regs` - If specified, heuristic calling convention detection will only consider this calling convention if the function clobbers all the registers specified here.
+* `stack_args_naturally_aligned` - For stack arguments, set this to True if the convention pads each stack slot up to the natural alignment of its type rather than the address-size word. Defaults to False.
+* `is_return_type_reg_compatible` — Returns True if a return value of a type fits in the convention's return register(s). Override when your ABI accepts only a subset of widths or excludes structures/arrays even when they would technically fit. Used by analysis to decide whether to use an indirect return.
+* `is_arg_type_reg_compatible` — Returns True if a parameter value of a type fits in the convention's parameter register(s).
+* `is_non_reg_arg_indirect` — Returns True if a parameter that does not fit in registers should be passed by pointer instead of pushed on the stack.
+* `get_indirect_return_value_location` — Returns the `Variable` that holds the caller-supplied pointer to the return value storage when the return value is too big for registers. Defaults to the first integer argument register.
+* `get_returned_indirect_return_value_pointer` — Returns the `Variable` that the *callee* uses to give the indirect return pointer back to the caller, or `None` if the convention does not return it.
+* `get_return_value_location` — Compute the return value location for a given `ReturnValue`.
+* `get_parameter_locations` — Compute the location for each parameter given the already resolved return value location (which the convention may need to skip past).
+* `get_stack_adjustment_for_locations` — Returns the number of bytes of stack adjustment performed by the called function on return. The default returns zero (caller cleans the stack).
+* `get_call_layout` — Compute a complete `CallLayout` (parameter locations, return value location, stack adjustment, register stack adjustments) for a function with the given signature. The default implementation composes the answer from `get_return_value_location`, `get_parameter_locations` and the stack adjustment helpers. Override only when the layout has interactions you can't express component-wise (Go's stack-after-args return slot is an example).
Then, we need to register the Calling Convention and tell the Platform and Architecture to use it:
diff --git a/docs/dev/bnil-hlil.md b/docs/dev/bnil-hlil.md
index ea006547..7443f192 100644
--- a/docs/dev/bnil-hlil.md
+++ b/docs/dev/bnil-hlil.md
@@ -79,6 +79,8 @@ There are a number of properties that can be queried on the [`HighLevelILInstruc
* `HLIL_SPLIT` - A split pair of variables `high`:`low` which can be used a single expression
* `HLIL_DEREF` - Dereferences `src`
* `HLIL_DEREF_FIELD` -
+* `HLIL_PASS_BY_REF` - Wraps `src` to indicate that the calling convention is passing a parameter by reference. The inner expression has the reference taken and has a pointer type. Only appears as a parameter expression on a call instruction.
+* `HLIL_RETURN_BY_REF` - Wraps `src` to indicate that the value is being returned indirectly through a caller-supplied pointer. The inner expression is the destination of the return value, not a pointer to it. Only appears on the left side of an assignment for the result of a call instruction.
### Arithmetic Operations
diff --git a/docs/dev/bnil-mlil.md b/docs/dev/bnil-mlil.md
index 9a6aa6a3..15fb836d 100644
--- a/docs/dev/bnil-mlil.md
+++ b/docs/dev/bnil-mlil.md
@@ -277,9 +277,8 @@ The parameter list can be accessed through the `params` property:
* `MLIL_JUMP` - Branch to the `dest` expression's address
* `MLIL_JUMP_TO` - A jump table dispatch instruction. Uses the `dest` expression to calculate the MLIL instruction target `targets` to branch to
-* `MLIL_CALL` - Branch to the `dest` expression function, saving the return address, with the list of parameters `params` and returning the list of return values `output`
+* `MLIL_CALL` - Branch to the `dest` expression function, saving the return address, with the list of parameters `params` and a list of output expressions `output_exprs` describing how each return value is delivered
* `MLIL_CALL_UNTYPED` - This is a call instruction where stack resolution could not be determined, and thus a list of parameters and return values do not exist
-* `MLIL_CALL_OUTPUT` - This expression holds a set of return values `dest` from a call
* `MLIL_CALL_PARAM` - This expression holds the set of parameters `src` for a call instruction
* `MLIL_RET` - Return to the calling function.
* `MLIL_RET_HINT` - Indirect jump to `dest` expression (only used in internal analysis passes.)
@@ -318,7 +317,7 @@ The parameter list can be accessed through the `params` property:
* `MLIL_FLOAT_CONST` - A floating point constant `constant`
* `MLIL_IMPORT` - A `constant` integral value representing an imported address
* `MLIL_LOW_PART` - `size` bytes from the low end of `src` expression
-
+* `MLIL_PASS_BY_REF` - Wraps `src` to indicate that the calling convention is passing a parameter by reference. The inner expression has the reference taken and has a pointer type. Only appears as a parameter expression on a call instruction.
### Arithmetic Operations
@@ -401,4 +400,14 @@ The parameter list can be accessed through the `params` property:
* `MLIL_UNIMPL` - The expression is not implemented
* `MLIL_UNIMPL_MEM` - The expression is not implemented but does access `src` memory
+### Function Call Outputs
+
+Prior to version 5.4, a function call could only return a list of variables as output. The `output` property on call instructions remains a list of variables, but a function call's `output_exprs` is a list of expressions that describe in more detail how each return value is delivered to the caller, and also adds support for indirect stores. The expressions in the list are one of:
+
+* `MLIL_VAR_OUTPUT` - a whole variable is written. The simplest, most common case.
+* `MLIL_VAR_OUTPUT_FIELD` - a field of a variable (at byte `offset`) is written. Used when the return value is placed into part of a larger structure.
+* `MLIL_STORE_OUTPUT` - the return value is stored to memory at the given destination expression. Used for indirect returns that do not target a local variable.
+
+Additionally, a return value can be the following expression, wrapping one of the above:
+* `MLIL_RETURN_BY_REF` - Wraps `src` to indicate that the value is being returned indirectly through a caller-supplied pointer. The inner expression will be one of the `MLIL_VAR_OUTPUT`, `MLIL_VAR_OUTPUT_FIELD`, or `MLIL_STORE_OUTPUT` instructions.
diff --git a/docs/guide/types/attributes.md b/docs/guide/types/attributes.md
index c8040e70..24bb9848 100644
--- a/docs/guide/types/attributes.md
+++ b/docs/guide/types/attributes.md
@@ -137,12 +137,73 @@ The following built-in calling conventions without dedicated keywords are availa
|`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|
+|`pascal`|x86|Pascal stack-based convention with left-to-right parameter passing and callee stack cleanup|
|`register`|x86|Register-based calling convention with left-to-right parameter passing (used by default in Delphi)|
|`gcc-fastcall`|x86|The `fastcall` calling convention as implemented in GCC on non-Windows platforms|
|`clang-fastcall`|x86|The `fastcall` calling convention as implemented in Clang on non-Windows platforms|
|`gcc-thiscall`|x86|The `thiscall` calling convention as implemented in GCC on non-Windows platforms|
|`clang-thiscall`|x86|The `thiscall` calling convention as implemented in Clang on non-Windows platforms|
+???+ Warning "Linux x86 / x86_64 default convention rename"
+ Prior to version 5.4, the default Linux convention on x86/x86_64 was named `cdecl` (and the stdcall variant was `stdcall`). It is now `sysv` (and `sysv-stdcall`) to deconflict with the Windows behavior of `cdecl`/`stdcall`. Both names continue to be registered on the architecture, so `__convention("cdecl")` will still resolve to the Windows version of `cdecl` even on Linux. If you have scripts that match calling conventions by string name, update them to recognize `sysv` and `sysv-stdcall`.
+
+## Custom Parameter and Return Value Locations
+
+Calling conventions describe the default placement of parameters and return values, but many real-world ABIs have functions whose locations diverge from those defaults (for example, hand-tuned assembly, custom register conventions, or high-level language features). You can override the default location for individual parameters with the `@` syntax or for a function's return value with the `__location("...")` attribute. The argument is a string in Binary Ninja's value-location syntax (described below).
+
+### Examples
+
+``` C
+/* Parameter locations: place this parameter in a specific register or stack slot */
+int foo(int reg_param @ rdi, int stack_param @ 0x10);
+
+/* Return-value location: return through rsi instead of the default rax */
+int bar() __location("rsi");
+
+/* A 16-byte value returned with the high half in rdx and the low half in rax;
+ components are written left-to-right from high to low */
+struct pair get_pair() __location("rdx:rax");
+
+/* A parameter value in two registers. Complex locations for parameters are quoted. */
+struct void set_pair(struct pair value @ "rdx:rax");
+
+/* A return value spanning two registers with explicit field offsets */
+struct mixed get_mixed() __location("[0x0: rax, 0x8: xmm0]");
+
+/* An indirect return through a caller-supplied pointer; the leading * marks the
+ location as a pointer to the storage, and "-> *rax" says the same pointer is
+ returned in rax */
+struct big get_big() __location("*rdi -> *rax");
+```
+
+### Value Location Syntax
+
+The string that makes up a location describes one or more storage components (the locations holding the bytes of a single value). The grammar is:
+
+* **Register component:** the register name, e.g., `rax`, `xmm0`, `r1`.
+* **Stack component:** an integer offset into the caller's stack frame (decimal or `0x`-prefixed hex), e.g., `0x10`, `-4`.
+* **Component size suffix:** append `.b`, `.w`, `.d`, `.q`, `.t`, or `.o` for 1/2/4/8/10/16-byte sizes, or `.<n>` for an explicit byte count, e.g., `eax.d`, `r0.q`, `rax.b`. Without a suffix, the natural register width is used (for stack components, sizes are inferred from the type).
+* **Multi-component (concatenated):** components separated by `:`, written **high-to-low**, e.g., `rdx:rax` puts the low half in `rax` and the high half in `rdx`. This form requires that components are contiguous.
+* **Multi-component with offsets:** when components are not contiguous, list them inside `[ ... ]` as `offset: component`, e.g., `[0x0: rax, 0x8: xmm0]`. Offsets are byte offsets within the value being passed/returned.
+* **Indirect:** prefix the entire location with `*` to indicate the location holds a pointer to the value rather than the value itself, e.g., `*rdi`.
+* **Returned-pointer hint:** for indirect returns where the same pointer is also returned in a register, append `-> *<reg>`, e.g., `*rdi -> *rax`.
+
+### Pass By Value and By Reference
+
+For composite types (structures, arrays) the calling convention decides whether to pass the value packed into registers, on the stack, or indirectly through a pointer. When that default is wrong for a particular declaration (most commonly in C++ where non-trivial type rules are applied that cannot always be determined at the binary level) you can override it with `__by_value` or `__by_ref`:
+
+``` C
+/* Force this argument to be passed by value (in registers or on the stack)
+ even when the convention would normally pass it indirectly */
+void takes_value(struct value_type __by_value arg);
+
+/* Force this argument to be passed by reference (as a pointer) even when the
+ convention would normally pass it by value */
+void takes_object(struct object_type __by_ref arg);
+```
+
+`__by_value` and `__by_ref` apply per-parameter and affect only the location chosen for the parameter (the parameter's type in the signature is unchanged). If you need to override the *exact* register or stack slot, use the `@` syntax or `__location()` attribute described above instead (it implies a custom location and overrides any by-value/by-ref decision).
+
## 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 14d9d0cc..a76f59f0 100644
--- a/python/callingconvention.py
+++ b/python/callingconvention.py
@@ -86,6 +86,51 @@ class CallLayout:
class CallingConvention:
+ """
+ ``class CallingConvention`` describes how parameters, return values, and the stack are handled
+ when a function is called. Subclasses of ``CallingConvention`` define the behavior of a calling
+ convention by setting the class attributes below and, where necessary, overriding the methods.
+
+ :cvar name: The name of this calling convention.
+ :cvar caller_saved_regs: The list of registers that are not preserved across a call
+ (caller-saved / volatile registers).
+ :cvar callee_saved_regs: The list of registers that a callee must preserve across a call
+ (callee-saved / non-volatile registers).
+ :cvar int_arg_regs: The registers used to pass integer and pointer arguments, in the order
+ they are used.
+ :cvar float_arg_regs: The registers used to pass floating point arguments, in the order they
+ are used.
+ :cvar required_arg_regs: The set of registers that must be arguments for heuristic calling
+ convention detection to consider this calling convention as a valid option.
+ :cvar required_clobbered_regs: The set of registers that must be clobbered for heuristic
+ calling convention detection to consider this calling convention as a valid option.
+ :cvar arg_regs_share_index: Whether the integer and floating point argument registers share a
+ single argument index. When ``True``, the Nth argument consumes the Nth slot of both the
+ integer and float register lists regardless of its type. When ``False``, integer and float
+ arguments are assigned from their respective register lists independently.
+ :cvar arg_regs_for_varargs: Whether argument registers are used to pass variadic arguments.
+ :cvar stack_reserved_for_arg_regs: Whether stack space is reserved by the caller for the
+ register arguments (for example, the shadow/home space used by the Windows x64 calling
+ convention).
+ :cvar stack_adjusted_on_return: Whether the callee adjusts the stack to remove the arguments
+ before returning (as in stdcall), rather than leaving the caller to clean up the stack (as
+ in cdecl).
+ :cvar eligible_for_heuristics: Whether this calling convention may be selected by heuristic
+ calling convention detection.
+ :cvar int_return_reg: The register that holds the integer return value.
+ :cvar high_int_return_reg: The register that holds the high part of an integer return value
+ that is too large to fit in a single register, or ``None`` if there is none.
+ :cvar float_return_reg: The register that holds the floating point return value, or ``None``
+ if there is none.
+ :cvar global_pointer_reg: The register that holds the global pointer, if the calling
+ convention defines one, or ``None`` if there is none.
+ :cvar implicitly_defined_regs: The registers that are implicitly given a known value on
+ function entry by this calling convention.
+ :cvar stack_args_naturally_aligned: Whether arguments passed on the stack are aligned to their
+ natural alignment. If ``False``, arguments are aligned to the address size.
+ :cvar stack_args_pushed_left_to_right: Whether arguments passed on the stack are pushed
+ left-to-right, as opposed to the more common right-to-left order.
+ """
name = None
caller_saved_regs = []
callee_saved_regs = []
@@ -867,46 +912,150 @@ class CallingConvention:
def get_incoming_reg_value(
self, reg: 'architecture.RegisterName', func: 'function.Function'
) -> 'variable.RegisterValue':
+ """
+ ``get_incoming_reg_value`` gets the known value of a register on entry to a function.
+
+ :param RegisterName reg: register to query
+ :param Function func: function being analyzed
+ :return: the incoming value of the register
+ :rtype: RegisterValue
+ """
return self.perform_get_incoming_reg_value(reg, func)
def get_incoming_flag_value(
self, reg: 'architecture.RegisterName', func: 'function.Function'
) -> 'variable.RegisterValue':
+ """
+ ``get_incoming_flag_value`` gets the known value of a flag on entry to a function.
+
+ :param reg: flag to query
+ :param Function func: function being analyzed
+ :return: the incoming value of the flag
+ :rtype: RegisterValue
+ """
return self.perform_get_incoming_flag_value(reg, func)
def get_incoming_var_for_parameter_var(
self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None
) -> 'variable.CoreVariable':
+ """
+ ``get_incoming_var_for_parameter_var`` gets the incoming variable that corresponds to the
+ given parameter variable. This is the inverse of :py:meth:`get_parameter_var_for_incoming_var`.
+
+ :param CoreVariable in_var: parameter variable
+ :param Function func: function being analyzed
+ :return: the incoming variable corresponding to the parameter variable
+ :rtype: CoreVariable
+ """
return self.perform_get_incoming_var_for_parameter_var(in_var, func)
def get_parameter_var_for_incoming_var(
self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None
) -> 'variable.CoreVariable':
+ """
+ ``get_parameter_var_for_incoming_var`` gets the parameter variable that corresponds to the
+ given incoming variable. This is the inverse of :py:meth:`get_incoming_var_for_parameter_var`.
+
+ :param CoreVariable in_var: incoming variable
+ :param Function func: function being analyzed
+ :return: the parameter variable corresponding to the incoming variable
+ :rtype: CoreVariable
+ """
return self.perform_get_incoming_var_for_parameter_var(in_var, func)
def is_return_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool:
+ """
+ ``is_return_type_reg_compatible`` determines whether a value of the given type can be
+ returned in registers, as opposed to being returned indirectly through memory.
+
+ :param BinaryView view: binary view providing type information
+ :param Type type: return type to check
+ :return: whether the return type is register compatible
+ :rtype: bool
+ """
return self.default_is_return_type_reg_compatible(type)
def is_non_reg_arg_indirect(self, view: Optional['binaryview.BinaryView'], type: Optional['types.Type']) -> bool:
+ """
+ ``is_non_reg_arg_indirect`` determines whether an argument that cannot be passed in
+ registers is passed indirectly by pointer as opposed to being passed directly on the stack.
+
+ :param BinaryView view: binary view providing type information
+ :param Type type: argument type to check
+ :return: whether the non-register argument is passed indirectly by pointer
+ :rtype: bool
+ """
return False
def default_is_return_type_reg_compatible(self, type: 'types.Type') -> bool:
+ """
+ ``default_is_return_type_reg_compatible`` is the default implementation of
+ :py:meth:`is_return_type_reg_compatible`. The default implementation allows register
+ returns for types that fit in a single register, have a size equal to two registers when
+ ``high_int_return_reg`` is set, or are a floating point type when ``float_return_reg`` is set.
+
+ :param Type type: return type to check
+ :return: whether the return type is register compatible
+ :rtype: bool
+ """
return core.BNDefaultIsReturnTypeRegisterCompatible(self.handle, type.handle)
def get_indirect_return_value_location(self) -> 'variable.CoreVariable':
+ """
+ ``get_indirect_return_value_location`` gets the location used to pass the hidden pointer
+ argument for return values that are returned indirectly through memory.
+
+ :return: the location of the indirect return value pointer
+ :rtype: CoreVariable
+ """
return self.get_default_indirect_return_value_location()
def get_default_indirect_return_value_location(self) -> 'variable.CoreVariable':
+ """
+ ``get_default_indirect_return_value_location`` is the default implementation of
+ :py:meth:`get_indirect_return_value_location`. The default location is the first integer
+ argument register, or the first stack slot if there are no integer argument registers.
+
+ :return: the location of the indirect return value pointer
+ :rtype: CoreVariable
+ """
result = core.BNGetDefaultIndirectReturnValueLocation(self.handle)
return variable.CoreVariable.from_BNVariable(result)
def get_returned_indirect_return_value_pointer(self) -> Optional['variable.CoreVariable']:
+ """
+ ``get_returned_indirect_return_value_pointer`` gets the location in which the hidden
+ indirect return value pointer is returned to the caller, for calling conventions that
+ return it.
+
+ :return: the location the indirect return value pointer is returned in, or ``None`` if it is not returned
+ :rtype: Optional[CoreVariable]
+ """
return None
def is_arg_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool:
+ """
+ ``is_arg_type_reg_compatible`` determines whether a value of the given type can be passed
+ as an argument in registers.
+
+ :param BinaryView view: binary view providing type information
+ :param Type type: argument type to check
+ :return: whether the argument type is register compatible
+ :rtype: bool
+ """
return self.default_is_arg_type_reg_compatible(type)
def default_is_arg_type_reg_compatible(self, type: 'types.Type') -> bool:
+ """
+ ``default_is_arg_type_reg_compatible`` is the default implementation of
+ :py:meth:`is_arg_type_reg_compatible`. The default implementation allows register arguments
+ for types that fit in a single register, or are a floating point type when ``float_arg_regs``
+ has valid registers.
+
+ :param Type type: argument type to check
+ :return: whether the argument type is register compatible
+ :rtype: bool
+ """
return core.BNDefaultIsArgumentTypeRegisterCompatible(self.handle, type.handle)
def get_call_layout(
@@ -914,6 +1063,28 @@ class CallingConvention:
params: 'types.ParamsType', func: Optional['function.Function'] = None,
permitted_regs: Optional[List['architecture.RegisterIndex']] = None
) -> 'CallLayout':
+ """
+ ``get_call_layout`` computes the complete call layout (parameter locations, return value
+ location, and stack adjustments) for a call with the given return value and parameters. It
+ is recommended to only override this method if the calling convention behavior cannot be
+ modeled with :py:meth:`get_return_value_location` and/or :py:meth:`get_parameter_locations`.
+
+ The default implementation calls :py:meth:`get_default_call_layout`.
+
+ When calling this method to query the layout of a function, the return value and parameters
+ should have their named type references dereferenced before passing them to this method.
+ Calling the methods :py:meth:`BinaryView.deref_return_value_named_type_references` and
+ :py:meth:`BinaryView.deref_parameter_named_type_references` will perform this dereferencing.
+
+ :param BinaryView view: binary view providing type information
+ :param return_value: return value of the call
+ :param params: parameters of the call
+ :param Function func: function used to resolve the architecture of the resulting locations
+ :param permitted_regs: optional set of register indices that argument passing is restricted \
+ to; if not provided, the calling convention's default registers are used
+ :return: the computed call layout
+ :rtype: CallLayout
+ """
return self.get_default_call_layout(view, return_value, params, func, permitted_regs)
def get_default_call_layout(
@@ -921,6 +1092,21 @@ class CallingConvention:
params: 'types.ParamsType', func: Optional['function.Function'] = None,
permitted_regs: Optional[List['architecture.RegisterIndex']] = None
) -> 'CallLayout':
+ """
+ ``get_default_call_layout`` is the default implementation of :py:meth:`get_call_layout`. The
+ default implementation uses :py:meth:`get_return_value_location`,
+ :py:meth:`get_parameter_locations`, :py:meth:`get_stack_adjustment_for_locations`, and
+ :py:meth:`get_register_stack_adjustments` to compute the layout.
+
+ :param BinaryView view: binary view providing type information
+ :param return_value: return value of the call
+ :param params: parameters of the call
+ :param Function func: function used to resolve the architecture of the resulting locations
+ :param permitted_regs: optional set of register indices that argument passing is restricted \
+ to; if not provided, the calling convention's default registers are used
+ :return: the computed call layout
+ :rtype: CallLayout
+ """
if view is None:
view_obj = None
else:
@@ -947,11 +1133,36 @@ class CallingConvention:
def get_return_value_location(
self, view: Optional['binaryview.BinaryView'], return_value: 'types.ReturnValueOrType'
) -> Optional['types.ValueLocation']:
+ """
+ ``get_return_value_location`` computes the location of the return value for the given return
+ value type and location structure.
+
+ The default implementation calls :py:meth:`get_default_return_value_location`.
+
+ :param BinaryView view: binary view providing type information
+ :param return_value: return value to compute the location for
+ :return: the location of the return value
+ :rtype: Optional[ValueLocation]
+ """
return self.get_default_return_value_location(view, return_value)
def get_default_return_value_location(
self, view: Optional['binaryview.BinaryView'], return_value: 'types.ReturnValueOrType'
) -> Optional['types.ValueLocation']:
+ """
+ ``get_default_return_value_location`` is the default implementation of
+ :py:meth:`get_return_value_location`. The default implementation checks
+ :py:meth:`is_return_type_reg_compatible` and places the return value in registers if it
+ can, or uses an indirect return by pointer if not. If an indirect return is required, then
+ :py:meth:`get_indirect_return_value_location` and
+ :py:meth:`get_returned_indirect_return_value_pointer` are used to provide the location of
+ the indirect return value.
+
+ :param BinaryView view: binary view providing type information
+ :param return_value: return value to compute the location for
+ :return: the location of the return value
+ :rtype: Optional[ValueLocation]
+ """
if view is None:
view_obj = None
else:
@@ -975,6 +1186,22 @@ class CallingConvention:
params: 'types.ParamsType', arch: Optional['architecture.Architecture'] = None,
permitted_regs: Optional[List['architecture.RegisterIndex']] = None
) -> List['types.ValueLocation']:
+ """
+ ``get_parameter_locations`` computes the locations of the parameters for a call with the
+ given return value and parameters.
+
+ The default implementation calls :py:meth:`get_default_parameter_locations`.
+
+ :param BinaryView view: binary view providing type information
+ :param return_value: optional location of the return value, which may affect parameter \
+ placement (for example, when an indirect return pointer consumes an argument register)
+ :param params: parameters of the call
+ :param Architecture arch: architecture used to resolve the resulting locations
+ :param permitted_regs: optional set of register indices that argument passing is restricted \
+ to; if not provided, the calling convention's default registers are used
+ :return: the locations of the parameters, in order
+ :rtype: List[ValueLocation]
+ """
return self.get_default_parameter_locations(view, return_value, params, arch, permitted_regs)
def get_default_parameter_locations(
@@ -982,6 +1209,27 @@ class CallingConvention:
params: 'types.ParamsType', arch: Optional['architecture.Architecture'] = None,
permitted_regs: Optional[List['architecture.RegisterIndex']] = None
) -> List['types.ValueLocation']:
+ """
+ ``get_default_parameter_locations`` is the default implementation of
+ :py:meth:`get_parameter_locations`. The default implementation uses ``int_arg_regs``,
+ ``float_arg_regs``, ``arg_regs_share_index``, ``stack_reserved_for_arg_regs``,
+ :py:meth:`is_arg_type_reg_compatible`, :py:meth:`is_non_reg_arg_indirect`,
+ ``stack_args_naturally_aligned``, and ``stack_args_pushed_left_to_right`` to compute
+ the parameter layout.
+
+ This function is usually sufficient unless the calling convention has unusual parameter
+ passing behavior. Most calling conventions can be defined per-argument using the attributes
+ and methods listed above.
+
+ :param BinaryView view: binary view providing type information
+ :param return_value: optional location of the return value
+ :param params: parameters of the call
+ :param Architecture arch: architecture used to resolve the resulting locations
+ :param permitted_regs: optional set of register indices that argument passing is restricted \
+ to; if not provided, the calling convention's default registers are used
+ :return: the locations of the parameters, in order
+ :rtype: List[ValueLocation]
+ """
if view is None:
view_obj = None
else:
@@ -1010,9 +1258,32 @@ class CallingConvention:
def get_parameter_ordering_for_variables(
self, view: Optional['binaryview.BinaryView'], params: Dict['variable.CoreVariable', 'types.Type']
) -> List['variable.CoreVariable']:
+ """
+ ``get_parameter_ordering_for_variables`` computes the order in which the given parameter
+ variables are passed. Used by the heuristic calling convention detection to create a
+ function type from a list of parameter variables.
+
+ The default implementation calls :py:meth:`get_default_parameter_ordering_for_variables`.
+
+ :param BinaryView view: binary view providing type information
+ :param params: map of parameter variables to their types
+ :return: the parameter variables in the order they are passed
+ :rtype: List[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']:
+ """
+ ``get_default_parameter_ordering_for_variables`` is the default implementation of
+ :py:meth:`get_parameter_ordering_for_variables`. The default implementation first checks
+ ``arg_regs_share_index`` to see if the parameter ordering is well defined. If the arguments
+ do not share an index, it places all integer arguments before the floating point arguments.
+ Arguments that are not passed in a normal location are placed last.
+
+ :param params: map of parameter variables to their types
+ :return: the parameter variables in the order they are passed
+ :rtype: List[CoreVariable]
+ """
vars = (core.BNVariable * len(params))()
types = (ctypes.POINTER(core.BNType) * len(params))()
for (i, (var, ty)) in enumerate(params.items()):
@@ -1033,12 +1304,36 @@ class CallingConvention:
self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'],
params: List[Tuple['types.ValueLocation', 'types.Type']]
):
+ """
+ ``get_stack_adjustment_for_locations`` computes the stack adjustment applied on return for
+ a call with the given return value and parameter locations.
+
+ The default implementation calls :py:meth:`get_default_stack_adjustment_for_locations`.
+
+ :param BinaryView view: binary view providing type information
+ :param return_value: optional location of the return value
+ :param params: list of ``(location, type)`` tuples for the parameters
+ :return: the stack adjustment in bytes
+ :rtype: int
+ """
return self.get_default_stack_adjustment_for_locations(return_value, params)
def get_default_stack_adjustment_for_locations(
self, return_value: Optional['types.ValueLocation'],
params: List[Tuple['types.ValueLocation', 'types.Type']]
):
+ """
+ ``get_default_stack_adjustment_for_locations`` is the default implementation of
+ :py:meth:`get_stack_adjustment_for_locations`. The default implementation first checks
+ ``stack_adjusted_on_return``, and returns zero if that is ``False``. Otherwise, it checks
+ the stack parameter locations and ``stack_args_naturally_aligned`` to compute the stack
+ adjustment necessary to cover all parameters.
+
+ :param return_value: optional location of the return value
+ :param params: list of ``(location, type)`` tuples for the parameters
+ :return: the stack adjustment in bytes
+ :rtype: int
+ """
if return_value is None:
ret = None
else:
@@ -1054,11 +1349,34 @@ class CallingConvention:
self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'],
params: List['types.ValueLocation']
) -> Dict['architecture.RegisterIndex', int]:
+ """
+ ``get_register_stack_adjustments`` computes the per-register-stack adjustments (for
+ architectures with register stacks, such as the x87 floating point stack) for a call with
+ the given return value and parameter locations.
+
+ The default implementation calls :py:meth:`get_default_register_stack_adjustments`.
+
+ :param BinaryView view: binary view providing type information
+ :param return_value: optional location of the return value
+ :param params: locations of the parameters
+ :return: a map from register stack index to its adjustment
+ :rtype: Dict[RegisterIndex, int]
+ """
return self.get_default_register_stack_adjustments(return_value, params)
def get_default_register_stack_adjustments(
self, return_value: Optional['types.ValueLocation'], params: List['types.ValueLocation']
) -> Dict['architecture.RegisterIndex', int]:
+ """
+ ``get_default_register_stack_adjustments`` is the default implementation of
+ :py:meth:`get_register_stack_adjustments`. The default implementation compares the register
+ stack slots used by the parameters and the return value to compute the adjustments.
+
+ :param return_value: optional location of the return value
+ :param params: locations of the parameters
+ :return: a map from register stack index to its adjustment
+ :rtype: Dict[RegisterIndex, int]
+ """
if return_value is None:
ret = None
else:
@@ -1085,6 +1403,13 @@ class CallingConvention:
@property
def arch(self) -> 'architecture.Architecture':
+ """
+ The architecture this calling convention applies to.
+
+ :getter: returns the architecture this calling convention applies to
+ :setter: sets the architecture this calling convention applies to
+ :type: Architecture
+ """
return self._arch
@arch.setter