# Linux x64 Calling Convention: Stack Frame

## Linux x64 Calling Convention: Stack Frame

### TL; DR

In 64-bit Linux system, function arguments of type integer/pointers are passed to the callee function in the following way:

* Arguments 1-6 are passed via registers RDI, RSI, RDX, RCX, R8, R9 respectively;
* Arguments 7 and above are pushed on to the stack.

Once inside the callee function:

* Arguments 1-6 are accessed via registers RDI, RSI, RDX, RCX, R8, R9 before they are modified or via offsets from the RBP register like so: `rbp - $offset`. For example, if the first argument passed to the callee is `int` (4 bytes) and there are no local variables defined in the function, we could access it via `rbp - 0x4`;
* It's worth noting, that:
  * if the 1st argument was 8 bytes (for example, `long int`), we'd access it via `rbp - 0x8`;
  * if the callee function had 1 local variable defined that is smaller or equal to 16 bytes, the first argument of type `int` would be accessed via `rbp - (0x10 + 0x4)` or simply `rbp - 0x14`;
  * if the callee function had more than 16 bytes reserved for local variables, we'd access the first argument of type `int` via `rbp - 0x24`, which suggests that with every 16 bytes worth of local variables defined, the first argument is shifted by 0x10 bytes as shown here;
* Argument 7 can be accessed via `rbp + 0x10`, argument 8 via `rbp + 0x18` and so on.

{% hint style="warning" %}
Conclusions listed above are based on the code sample and screenshots provided in the below sections.
{% endhint %}

### Code

This lab and conclusions are based on the following C program compiled on a 64-bit Linux machine:

{% tabs %}
{% tab title="stack.c" %}

```cpp
#include <stdio.h>

int test(int a, int b, int c, int d, int e, int f, int g, int h, int i)
{
    //int a2 = 0x555577;
    return 1;
}

int main(int argc, char *argv[])
{
    test(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9);
    return 1;
}

// compile with gcc stack.c -o stack
```

{% endtab %}
{% endtabs %}

### How Arguments Are Passed

Let's now see how arguments are passed from a caller to callee.

Below is a screenshot that shows where the 9 arguments `0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9` passed to the function `test(int a, int b, int c, int d, int e, int f, int g, int h, int i)` end up in registers and the stack:

![](https://386337598-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFEMnER3fywgFHoroYn%2F-M_kPdFv-dvxpx8KxFzD%2F-M_kPmfxo1wKpMfYNvkM%2Fimage.png?alt=media\&token=6507484d-b6b9-43b8-b9aa-64167c14aba1)

Below is a table that complements the above screenshot and shows where arguments live in registers and on the stack and how they get there:

| Argument # | Location   | Variable | Value | Colour |
| ---------- | ---------- | -------- | ----- | ------ |
| 1          | RDI        | a        | 0x1   | Red    |
| 2          | RSI        | b        | 0x2   | Red    |
| 3          | RDX        | c        | 0x3   | Red    |
| 4          | RCX        | d        | 0x4   | Red    |
| 5          | R8         | e        | 0x5   | Orange |
| 6          | R9         | f        | 0x6   | Orange |
| 7          | RSP + 0x10 | g        | 0x7   | Lime   |
| 8          | RSP + 0x18 | h        | 0x8   | Lime   |
| 9          | RSP + 0x20 | i        | 0x9   | Lime   |

{% hint style="info" %}
Same applies to arguments that are memory addresses/pointers.
{% endhint %}

### Stack Inside test()

Below shows how function's `test` stack frame looks like on a 64-bit platform:

![Stack frame x64 inside the function test()](https://386337598-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFEMnER3fywgFHoroYn%2F-M_FSDz0eW-q9SPnN59k%2F-M_kLeQlfTmEOMRm-lqx%2Fimage.png?alt=media\&token=75d8acce-f92d-4e59-b9a1-dce3708552e0)

Again, note the following:

* Arguments 1 - 6 are moved through the registers `edi`, `esi`, `edx`, `ecx`, `r8d`, `r9d` (orange);
* Arguments 7 - 9 are pushed to the stack via `push` (blue);

#### Accessing the 1st Argument & Local Variables

Until now, our `test()` function did not have any local variables defined, so let's see how the stack changes once we have some variables and how we can access them.

If the callee had a local variable defined, such as `int a1 = 0x555577` (4 bytes, lime) as in our case shown below (lime), we'd access the first argument not via `rbp - 0x4` as it was the case previously when the callee had no local variables, but via `rbp - 0x14` (i.e it shifted by 0x10 bytes, red):

![First argument (red) is now shifted by 0x10 on the stack and can be accessed via rbp - 0x14](https://386337598-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFEMnER3fywgFHoroYn%2F-M_kNqvXp4R_-HrBZCWm%2F-M_kNunKFLZR3V1MAWIi%2Fimage.png?alt=media\&token=76059a52-3306-4664-ae35-4819d60b6328)

Based on the above case, the `test()` function stack frame, would now look like this:

![64-bit stack frame with 1 local variable defined inside the callee function](https://386337598-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFEMnER3fywgFHoroYn%2F-M_FSDz0eW-q9SPnN59k%2F-M_kH7I72thVoXdjJEvP%2Fimage.png?alt=media\&token=a2028640-7d8b-44f4-847b-27963b96a25d)

{% hint style="warning" %}
Note that the 1st argument, that we previously could access via `rbp - 0x4` has been shifted up by 0x10 bytes and is now accessible via `rbp - 0x14` whereas the local variable is now at `rbp - 0x4` (where the 1st argument was when the function did not have a local variable defined) followed by 0x10 bytes of padding.
{% endhint %}

Following the same principle as outlined above, if the callee had more than 16 bytes of local variables defined (17 bytes in our case as shown in the below screenshot), we'd now access the first argument via `rbp - 0x24` (i.e another 0x10 bytes shift from `rbp - 0x14`):

![First argument is shifted by 0x10 once again and can be accessed via rbp - 0x24](https://386337598-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFEMnER3fywgFHoroYn%2F-M_BEHP8NBlaa8q2mghX%2F-M_BOpjNIKjs04vJtJ37%2Fimage.png?alt=media\&token=afc96efa-6ac0-44ff-ba5d-0f16d15dc7e1)

Similarly, if the callee had more than 32 bytes of local variables defined (33 bytes in our case as shown in the below screenshot), we'd now access the first argument via `rbp - 0x34` (i.e yet another 0x10 bytes shift):

![First argument is shifted by 0x10 once again and can be accessed via rbp - 0x34](https://386337598-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFEMnER3fywgFHoroYn%2F-M_BEHP8NBlaa8q2mghX%2F-M_BPWKY1Ch3wvC5cdoX%2Fimage.png?alt=media\&token=8cb829b8-1e30-4c94-83ee-c69470f463ff)

...and so on.

### State Inside main()

Below captures program's state once inside `main()`:

![RDI and RSI registers inside main() contain argument count and argument values](https://386337598-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFEMnER3fywgFHoroYn%2F-MY_OKOscf42SiQo0Ix3%2F-MYam6D1vxeOrr3Uaglp%2Fimage.png?alt=media\&token=08225e35-a5c3-4d46-ae95-8af90e154cde)

Note from the above screenshot:

* Lime - `RDI` contains the the count of arguments our program was launched with (`argc`);
* Orange - `RSI` contains the address to an array of arguments our program was run with (`argv[]`) and the first one (`argv[0]`), as expected, is always the full path to the program itself, which is `/home/kali/labs/stack/stack` in our case.

Also, if we check what's happening higher up at the stack, we will see that it contains the environment variables the program was started with:

![](https://386337598-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFEMnER3fywgFHoroYn%2F-MaOQ_f9wSIPgoX-abwq%2F-MaTcjMUW0CLGv7363Yq%2Fimage.png?alt=media\&token=0ef98aa2-9f0f-435e-8556-45e11be91178)

Combining all the above knowledge, we can get a general view of the stack layout:

![Stack layout for 64-bit program on 64-bit Linux system](https://386337598-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFEMnER3fywgFHoroYn%2F-MaTeRvboVjnErSpM-5Q%2F-MaThwaoo5UffZzrO0Jl%2Fimage.png?alt=media\&token=a2ed0a6f-3354-47a5-bda6-993e57dcb051)

### References

{% embed url="<https://revers.engineering/applied-re-the-stack/>" %}

{% embed url="<https://revers.engineering/applied-re-accelerated-assembly-p1/>" %}
