前幾天有提到,在 Linux x86-64 中,Function 的 Argument 通常會放在:
RDI
RSI
RDX
RCX
R8
R9
而 Return Value 通常會放在:
RAX
也有看過很多次:
ret
但一個 Function 到底是怎麼被呼叫的?
今天就來看看 Assembly 裡面的:
call
ret
先看一段簡單的 C Code:
int add(int a, int b) {
return a + b;
}
int main() {
return add(10, 20);
}
Compile 之後,可能會看到類似:
main:
mov esi, 20
mov edi, 10
call add
前面有提過:
RDI → 第一個 Argument
RSI → 第二個 Argument
所以可以理解成:
mov edi, 10
mov esi, 20
等價成
add(10, 20);
接著:
call add
就是呼叫:
add
這個 Function。
那:
call add
到底做了什麼?
可以先簡單理解成兩件事情:
1. 記住等等要回來的位置
2. 跳到 add Function
例如:
call add
mov ebx, eax
CPU 執行:
call add
之後會跳到:
add:
開始執行。
但是 add 執行完之後,還需要知道要回來繼續執行:
mov ebx, eax
所以 call 會先保存一個:
Return Address
也就是:
Function 執行完之後,要回到哪裡繼續執行。
這個 Return Address 會被放到:
Stack
Stack 的部分下一篇再詳細介紹。
有了 Return Address 之後,Function 執行完就可以使用:
ret
回到原本的位置。
例如:
add:
lea eax, [rdi+rsi]
ret
流程可以簡單想成:
main
|
| call add
v
add
|
| 計算結果
|
| ret
v
main 繼續執行
所以:
call
負責進入 Function。
而:
ret
負責回到原本呼叫 Function 的地方。
前面也有提到:
RAX → Return Value
例如:
add:
lea eax, [rdi+rsi]
ret
假設:
RDI = 10
RSI = 20
那:
lea eax, [rdi+rsi]
可以先理解成:
EAX = 10 + 20
所以最後:
EAX = 30
而 EAX / RAX 通常就是 Function 的 Return Value。
因此這段:
add:
lea eax, [rdi+rsi]
ret
就可以理解成:
return a + b;
Function Argument 放在哪個 Register、Return Value 放在哪裡,並不是隨便決定的。
這些規則通常會由:
Calling Convention
來定義。
在 Linux x86-64 常見的 System V AMD64 Calling Convention 中:
RDI → Argument 1
RSI → Argument 2
RDX → Argument 3
RCX → Argument 4
R8 → Argument 5
R9 → Argument 6
RAX → Return Value
例如:
test(1, 2, 3);
可以先想成:
RDI = 1
RSI = 2
RDX = 3
接著:
call test
Function 裡面就可以從這些 Register 取得 Argument。
不過不同 Architecture、Operating System 或 Calling Convention,規則可能不一樣。
目前先記住 Linux x86-64 這組就可以了。
再看一次:
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(10, 20);
return result;
}
可以先把 Assembly 簡化理解成:
main:
mov esi, 20
mov edi, 10
call add
ret
add:
lea eax, [rdi+rsi]
ret
整個流程就是:
10 → EDI
20 → ESI
|
v
call add
|
v
EDI + ESI
|
v
EAX
|
v
ret
所以當看到:
call function_name
可以先想:
準備 Argument
↓
進入 Function
↓
Function 執行
↓
結果放進 RAX
↓
ret 回去
今天可以先記得:
call
他是會:
保存 Return Address
+
跳到指定 Function
而:
ret
則會:
回到 Return Address
另外在 Linux x86-64 中:
RDI → Argument 1
RSI → Argument 2
RDX → Argument 3
RCX → Argument 4
R8 → Argument 5
R9 → Argument 6
RAX → Return Value
現在已經可以大概看懂一個 Function 是怎麼被呼叫的。
但前面提到:
Return Address
會被放到:
Stack
那 Stack 到底是什麼?
RSP、RBP 又是在做什麼?
Function 裡面的 Local Variable 又放在哪裡?
下一篇就來看看:Stack