iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
自我挑戰組

C 語言筆記系列 第 4

[C 語言筆記--Day04] C 語言的 function call 如何被組合語言實作

  • 分享至 

  • xImage
  •  

這篇文章是用 x86-64 的架構作為例子,因為多數人的電腦是用 x86-64 的架構
只要依照 在linux中看gcc產生出來的組合語言 這篇文章
就可以把 C 語言的檔案編議成 x86-64 的組合語言
編譯時請記得加上 -Og 這個 flag ,因為較低的優化程度比較好觀察 gcc 的行為

大綱:

  1. x86-64 有哪些 register
  2. 一個沒有回傳值、也沒有參數的 function
  3. 如何回傳值
  4. 如何傳參數
  5. 參數大於 6 個時該怎麼做?

x86-64 有哪些 register

registers of x86-64
如上圖,x86-64 共有 16 個 64-bit register ,(不過這張圖並沒有把 program counter 畫上去)

以圖中左上的 register 為例,
%rax 代表的是整個 64-bit ,
而寫 %eax 時,代表的是較低位元的 32-bit

除了用紅色標示的 %rsp 有特別的用途之外 (register stack pointer),
其他的 15 個 register 都算是 general purpose 的 register

一個沒有回傳值、也沒有參數的 function

這種狀況是最簡單的,考慮以下的 C 語言程式碼:

void callee()
{
    /*...
    ...*/
}

void caller()
{
    /*...
    ...*/

    callee();

    /*...
    ...*/
}

編譯成組合語言:

callee:
    ...
    ...
    ...
    ...
    ret     

caller:
    ...
    ...
    call    callee
    ...
    ...

call callee 這行代表讓 program counter 指到 callee 的位置
ret 這行代表回到 call callee 的下一行繼續執行
幾乎就跟 C 語言的運作邏輯相同

如何回傳值

回傳值的過程大概可以列成以下步驟:

  1. caller 呼叫 callee
  2. callee 執行到最後面時,把回傳值放到 %rax
  3. callee 執行 ret 回到 caller 繼續執行
  4. caller%rax 拿取回傳值(因為 callee 已經把回傳值放到%rax 了)

透過 callee 把回傳值放到 %rax
caller%rax 拿取回傳值的模式,
就成功的實作出 C 語言裡 function 回傳值的功能,
當然不用 %rax 用其他的 register 也可以
但習慣上就是會用 %rax
所以說 C 裡的 return value 並不是用硬體來實作的行為,而是一種軟體上的設計

考慮以下的 C 語言程式碼:

long callee()
{
    return -0x87;
}

long caller() 
{
    long result;
    result = callee() + 1;
    return result;
}

編譯出的組合語言:

callee:
    mov    $0xffffffffffffff79,%rax
    retq   

caller:
    callq  callee
    add    $0x1,%rax
    retq   

如何傳參數

傳參數的方法跟傳回傳值的方法差不多
就跟回傳時要約定好要用 %rax 來當中介一樣
傳參數也要約定好要用哪些 register 來中介:

第幾個參數|register
----------+-------
1|%rdi
2|%rsi
3|%rdx
4|%rcx
5|%r8
6|%r9

考慮以下的 C 語言程式碼:

long callee(long a1, long a2, long a3, long a4, long a5, long a6)
{
    return a1 + a2 + a3 + a4 + a5 + a6;
}

long caller() 
{
    long result, a1, a2, a3, a4, a5, a6;
    a1 = -1;
    a2 = -2;
    a3 = -3;
    a4 = -4;
    a5 = -5;
    a6 = -6;
    result = callee(a1, a2, a3, a4, a5, a6) - 0x87;
    return result;
}

編譯出的組合語言:

callee:
    add    %rsi,%rdi
    add    %rdx,%rdi
    add    %rcx,%rdi
    add    %r8,%rdi
    lea    (%rdi,%r9,1),%rax            (%rax = %rdi + %r9 * 1)
    retq   

caller:
    mov    $0xfffffffffffffffa,%r9      (two's complement)
    mov    $0xfffffffffffffffb,%r8
    mov    $0xfffffffffffffffc,%rcx
    mov    $0xfffffffffffffffd,%rdx
    mov    $0xfffffffffffffffe,%rsi
    mov    $0xffffffffffffffff,%rdi
    callq  40 <caller+0x2f>
    sub    $0x87,%rax
    retq   

參數大於 6 個時該怎麼做?

來不及發文了,這個就留到明天在說!

參考資料:

Computer Systems: A Programmer's Perspective, 3/E (CS:APP3e)


上一篇
[C 語言筆記--Day03] 解題紀錄:MIN-MEX Cut
下一篇
[C 語言筆記--Day05] C 語言的 function call 如何被組合語言實作 II
系列文
C 語言筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言