Skip to content
This repository has been archived by the owner on Jun 27, 2024. It is now read-only.

Fix x86_64 Sys-V ABI section #29

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions pages/calling_conventions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -136,28 +136,33 @@ Remaining registers "belong" to the called function.

==== Stack
The stack is always 16-byte aligned at function _call_: this requires the
stack to be aligned to 16 bytes before the `CALL`.
The push of the return address onto the stack by `CALL` is immediately succeeded
by the push of RBP onto the stack by the callee function, thereby re-aligning
the stack shortly after function entry.
stack to be aligned to 16 bytes before the call.

The call instruction itself pushes 8 bytes, so the called routine will need to push an additional 8 bytes if it wants to benefit from 16-byte stack alignment (for example, caling any nested procedures, or storing a 16-byte SSE Type on the stack).


==== Parameters
`RDI`, `RSI`, `RDX`, `RCX`, `R8`, `R9`, stack (`[RSP+0x00]`, `[RSP+0x08]` and so
on).
Integer parameters, and small structs or unions are passed in (up to 32-bytes): `RDI`, `RSI`, `RDX`, `RCX`, `R8`, `R9`, stack (`[RSP+0x00]`, `[RSP+0x08]` and so
on). Large values (greater than 64-bits, but less than 256-bits) are passed in multiple successive registers (up to 4), but if any part of the value uses the stack, the whole value does (a value will never partially be passed in registers and memory).

For example, given `struct large{long long a; long long b;};`, `void foo(int a, int b, int c, int d, struct large bar);` passes bar in R8 and R9, but `void foo(int a, int b, int c, int d, int e, struct large bar);` passes bar on the stack (and R9 is unused as a parameter register for this function).

If the parameter is of type `float` or `double`, it will be stored in the next
available SSE register (`XMM0`-`XMM7`).

`long double` and structs/unions larger than 32-bytes are passed by the caller allocating space, and passing a pointer to that space in place as an integer parameter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing full stop


For example, a function with the prototype
`void do_something(int a, float b, int c, int d, int e, float f)` would pass
parameters in `RDI`, `XMM0`, `R8`, `R9`, `[RSP+0x00]` and `XMM1` respectively.
A function calling this needs to have at least 32 bytes of stack to store the
parameters and align the stack upon call.
parameters in `RDI`, `XMM0`, `RSI`, `RDX`, `RCX`, and `XMM1` respectively.
As no parameters are passed on the stack, the caller would also have to push 8 bytes before calling the function (which will align the stack by pushing an additional 8 bytes),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

full stop, instead of comma


==== Return value
If the return value is an integer/struct/union whose size is less than or equal
than 64 bits, it is returned in `RAX`; otherwise, the struct is allocated by the
caller and a pointer to it is passed as the first parameter, similarly to the
than 64 bits, it is returned in `RAX`;
integer/struct/unions between 64 and 128 bits are returned in both `RDX` (high bits) and `RAX` (low bits);
floating-point values are returned in `xmm0`, with `long double` instead using `st(0)` (which must be popped by the caller before it returns).
Large structs are allocated by the caller and a pointer to it is passed as the first parameter, similarly to the
Microsoft x64 ABI.
Dissimilarly, the pointer is actually returned in `RAX` upon return.

Expand Down