When There Are More Than 6 Parameters, the Memory Stack Is "Forced" Into Use
Table of Contents
- When There Are More Than 6 Parameters, the Memory Stack Is “Forced” Into Use
- 1. Rule: first 6, then stack
- 2. Deep integration: an on-site experiment with C and assembly
- 3. Stack frame layout: what do the parameters look like?
- 4. Why is this crucial for learning Pwn?
When There Are More Than 6 Parameters, the Memory Stack Is “Forced” Into Use
When a function has more than 6 parameters, the CPU is like a pocket already stuffed full—it no longer has any extra “fast lanes” (registers) to hold new data. At that point, the CPU must rely on its “backup storage”: the stack.
In x86-64 Linux (System V ABI), this handling method is called mixed register-and-stack passing.
1. Rule: first 6, then stack
We can think of parameter passing as a priority queue:
-
The first 6 parameters: fly “first class” and are stored in
RDI,RSI,RDX,RCX,R8, andR9respectively. -
The 7th and later parameters: fly “economy class” and are pushed onto the memory stack in order.
2. Deep integration: an on-site experiment with C and assembly
Let’s write a “big function” with 8 parameters and see how the compiler arranges the troops.
C code
C
// 一个有 8 个参数的函数void complex_func(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) { int sum = a7 + a8; // 我们重点看最后两个参数}
int main() { complex_func(1, 2, 3, 4, 5, 6, 7, 8); return 0;}The caller’s (the main function’s) assembly logic
Before calling complex_func, the assembly instructions become like this:
Code segment
; --- 准备第 7 和 第 8 个参数 (内存栈) ---push 8 ; 将第 8 个参数压栈push 7 ; 将第 7 个参数压栈
; --- 准备前 6 个参数 (寄存器) ---mov r9d, 6 ; a6mov r8d, 5 ; a5mov ecx, 4 ; a4mov edx, 3 ; a3mov esi, 2 ; a2mov edi, 1 ; a1
; --- 正式调用 ---call complex_func3. Stack frame layout: what do the parameters look like?
After the function is called, the stack-top pointer changes. From inside complex_func, if it wants to find the 7th and 8th parameters, it must look them up in memory:
| Memory address (relative to RSP) | Content | Note |
|---|---|---|
[rsp + 0x0] | Return address | Return address automatically pushed by the call instruction |
[rsp + 0x8] | 7 | 7th parameter (a7) |
[rsp + 0x10] | 8 | 8th parameter (a8) |
[!TIP]
Why +0x8 and +0x10? > Because on a 64-bit system, each pushed parameter occupies 8 bytes. The first 8 bytes store the return address after the function finishes, so parameters must be looked up starting at offset 8.
4. Why is this crucial for learning Pwn?
For students studying binary security, this is core knowledge for building an ROP (Return-Oriented Programming) chain:
-
Differences in parameter locations: if the vulnerable function you want to use has only 1–2 parameters (such as
system("/bin/sh")), you only need to look for a gadget likepop rdi; ret. -
Exploiting stack overflows: if the function you want to use has more than 7 parameters (for example, some complex kernel functions), you cannot rely on controlling registers alone—you must also control the data layout on the stack, because the program will read its subsequent parameters from places like
[rsp + 8].
This mixed “register + stack” mechanism is actually the CPU’s balance between speed (registers) and generality (the stack can hold an unlimited number of parameters).
Advanced thought
Have you ever wondered why function parameters are pushed onto the stack “from right to left” (push 8 first, then 7)? This is actually related to a very famous feature in C: variadic functions (such as printf). Want to talk about the “design trick” behind this?