# 你所不知道的C語言:函式呼叫篇 contributed by < [`ofAlpaca`](https://github.com/ofAlpaca) > ###### tags: `CSIE5006` `Note` ### Nested function * C 語言不支援 nested function,其目的為簡化編譯器設計。 ```clike= void f() { // declaration of function g void g(); g(); // once exit the function, function g() // is out of scope. } ``` --- ### Process 和 C 程式的關聯 * Virtual Memory 與 C 語言角度的 memory: [source](http://www.study-area.org/cyril/opentools/opentools/x909.html) (注意 address 是降冪還是升冪) * Process 角度的 Memeory : ([source](https://manybutfinite.com/post/anatomy-of-a-program-in-memory/)) * ==在 ELF 裡頭為 section, 進入到 memory 後則以 process 的 segment 去看。== * 注意,這裡是 virtual memory address (VMA)! ([VMA 與 ELF 對應](https://www.jollen.org/blog/2007/01/process_vma.html)) * Stack : 由高位址長至低位址,儲存函式呼叫時個別 stack frame 的 local variables 與 return address 等。 * Heap : 由低位址長至高位址,動態記憶體配置。 * BSS segement: Block Started by Symbol ,尚未初始化的變數。 * Data segement : 已經被初始化後的變數。 * 在此宣告的變數會存在 **data segment** ,例如: `int content = 10` 。 * 但是宣告在此的 pointer 所指向的內容則不會,也就是說 `gonzo` 的內容 `God's own prototype` 會是放在 text segment 裡,只有 pointer 所存的**位址**會在 data segment 。 * Text segement : 存放使用者程式的 binary code 。 * 裡頭變數的排序不一定是遞增或遞減。 ![](https://i.imgur.com/DpZOmhb.png) --- ### [Object File](https://www.slideshare.net/jserv/helloworld-internals) * File Format : * 每個 OS 所使用的 object 檔格式不盡相同, Linux 使用的是 **ELF** (Executable and Linkable Format),Windows 使用的是 **PE** (Portable Executable)。 * 可用於紀錄 **executable file、object file、share object、core dump**。 * **.o** 檔 (object file) 是介於 compiling 和 linking 的檔案,經過編譯但還未連結為可執行檔。 * object file 中的 section 排列順序與 process 的 virtual memory address (VMA) 順序顛倒。 * MMU(memory management unit) : 負責處理 CPU 對記憶體的存取請求,例如虛擬位址到實體位址的轉換。 * user space process 會透過 MMU 從 virtual memory 映射至 physical memory,kernel space process 則是一直存在於 physical memory。 * File Content : (可以使用 `readelf` 去查看二進位檔的結構。) ![](https://i.imgur.com/rVX3KZI.png) 1. .text section : 程式碼的部分。 2. .data section : 宣告並且有賦值的變數。 3. .bss section : 只有宣告,尚未初始化的變數。 * Compilation and Linking: * 原始 .c 檔與 .h 檔經過編譯成為 .object 檔,最後再與其他 .object 檔或是 static lib 進行 linking 產生 .out 。 * Symbol: * 可使用 `nm` 去查看二進位檔的 `SYMBOL` 位置。 * Symbol name 包含 function name 、 variable name。 * 每個 .object 檔都有個 symbol table ,存放 symbol value 。 --- ### Static linking * Methods : 1. Accumulate : 所有 section 依序排放在一起。 2. Merge similar section : 根據 .text 、 .data 、 .bss section 同類的放在一起。 * **.a** 檔 : object 檔的彙集,可以透過 linker 來與其他 object 檔做連結,ex. libc.a 。 ### Dynamic linking * **.so** 檔 : 為 Dynamic Shared Objects , dynamic loader 會在程式的執行期間讀入 .so 檔至記憶體,並做 relocation 。 * 較省空間,(.so file in linux),當需要使用到其他的 lib 時才會 load 進 memory 。 * Program1 與 Program2 在執行期間才動態連結 dynamic shared lib 。 ![](https://i.imgur.com/E3eAEbJ.png) --- ### Parameter vs Argument * 兩者為形式上的不同,但都可稱為參數 * formal parameter 為型別與符號 ex. int x 。 * actual argument 為數值 ex. 5 。 --- reference: C 程式語言 page 4-5 --- ### [Stack](http://gghh.name/dibtp/2015/11/11/function-calls-in-c-practical-example.html) * 包含了 parameter 、 local variable 、 return address 等。 * rbp 與 rsp 都是 x86 架構的暫存器。 * **rbp (frame pointer)** : 指向目前 stack frame 的起始位址。 * **rsp (stack pointer)** : 指向目前 stack frame 的末端位址,會隨著 stack 增長而增加。 * **return address** : 當目前的 stack frame 結束後要回去的位址。 ![](https://i.imgur.com/gK1ijxy.png) 1. Call function,**push** return address 到 Stack (給 instruction pointer使用) 。 2. [Function prologue](https://manybutfinite.com/post/journey-to-the-stack/) : a. 先 `PUSH` 目前 `rbp` 的位址。 b. 將 `rsp` 複製至 `rbp` 。(兩者指向目前 stack frame 的起點) c. `rsp` 開始往下移動(由高位址往低位址),保留空間給 local variables 。 3. [Function epilogue](https://manybutfinite.com/post/epilogues-canaries-buffer-overflows/) : a. 將目前 `rbp` 複製至 `rsp` ,之前的 local variables 也被釋放了。(兩者指向目前 stack frame 的起點) b. **Pop** 上個 `rbp` 的位址,讓目前的 `rbp` 回復成上個 stack frame 的 `rbp` 。 c. **Pop** return address 讓 instruction pointer 指向 call function 後的下道指令。 --- ### Heap * `free()` 為釋放 pointer 所指向之記憶體空間,非 pointer 本身。 * `free()` 後 pointer 不會在指向任何記憶體,如果再次 `free()` 就會產生 error。 * 釋放後可將 pointer 指向 NULL,防止二次釋放。 :::warning :question: [ ==Homework== ] 為什麼 glibc 可以偵測出上述程式的 "double free or corruption" 呢?如何去管理 ::: :::success * source : [ glibc/malloc/malloc.c] #line 344 (https://github.com/lattera/glibc/blob/master/malloc/malloc.c) 可以下關鍵字 `_int_free` 來看 glibc 是如何實作 `free()` 。 * 講解 glibc 如何偵測 double free 前,需要先解釋幾個名詞 : * `struct malloc_chunk` : 一個 doubly linked list 的資料結構的節點,每個節點除了雙向指標外,還存有目前節點大小與前節點大小。 * `fastbins` : 是個存有最近釋放掉的 `malloc_chunk` 的 single linked list。 * `mchunkptr` : 是一個 pointer to `malloc_chunk`。 * 解釋 : 當一個 pointer 被 `free()` 時,會先透過 `mem2chunk` 來找到其對應的 `mchunkptr` , 接著會去 `fastbins` 裡比對 `mchunkptr` 所指向的位址是否已經存在,有則表示之前此位址已經被 `free()` 過了,故產生 "double free or corruption" 的 error message 。 ::: > 這裡有個有趣的點,你可以嘗試看看 free 兩個 fastbin 大小的 chunk,順序:A, B, A,看看會發生什麼事 > [name=HexRabbit][color=purple] :::info memory allocator 內部實作可參照春季班課程報告: [rpmalloc 探討](https://hackmd.io/s/H1TP6FV6z) :notes: jserv :::