Calling Conventions
Borland Register Convention
Also known as the Borland Fastcall Convention, and not to be confused with the Microsoft and GNU Fastcall conventions.
The Borland Register convention passes the first three parameters, left-to-right, in the order EAX, EDX, ECX
.
It then pushes the remaining parameters onto the stack, left-to-right.
In the Register convention, the callee cleans up the stack after it finishes running. The callee refers to the function being called, whereas the caller is the one doing the calling.
To show what this might look like, the following function takes in six numbers, adds them together, and returns them.
fn myFn(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32) -> u32 {
a + b + c + d + e + f
}
fn main() {
myFn(1, 2, 3, 4, 5, 6);
}
And in assembly, it looks like this.
; push the last arguments onto the stack, left-to-right.
push 4
push 5
push 6
; add the first 3 arguments into the registers EAX, EDX, ECX, left-to-right.
mov eax, 1
mov edx, 2
mov ecx, 3
call myFn ; the return value is passed to the EAX register
; We don't need to clean up the stack here, as the callee does it
Here, we push the last arguments (4, 5, 6)
onto the stack in the order they were written (left-to-right).
Then we pass the first three arguments (1, 2, 3)
into the EAX
, EDX
, and ECX
registers, also in the order they were written (left-to-right).
cdecl (C Declaration)
The C Declaration calling convention is a simple calling convention used in the C programming language.
This simple calling convention is ideal for hooking into borland functions, as it is very easy to push the general registers
that borland uses (EAX, EDX, ECX)
into the stack, run our code, and then pop them afterwards. We use this instead of writing functions using the register convention, because Rust does not currently support the register calling convention.
In cdecl, all arguments are pushed to the stack in right-to-left order, and the return value of the function is put in EAX
.
When talking about passing arguments, "left-to-right" refers to inserting the parameters in the order they were written.
Alternatively, "right-to-left" refers to inserting the parameters backwards.
So 1,2,3,4
would be passed backwards, as 4,3,2,1
.
Once the function finishes running, the caller cleans up the stack.
Using the previous example, the assembly would look like this.
; push all arguments onto the stack, right-to-left.
push 6
push 5
push 4
push 3
push 2
push 1
call myFn ; the return value is passed to the EAX register.
; on x87, returned floating point value is pushed into register ST0.
; in cdecl, the caller cleans the stack.
; by adding to the stack pointer (ESP), we remove these bytes from the stack.
add esp, 24 ; remove the call arguments from the stack pointer.
; 6 arguments, 4 bytes per argument. 4 x 6 = 24