---
tags: 你所不知道的 C 語言, 進階電腦系統理論與實作, NCKU Linux Kernel Internals, 作業系統
---
# 動態連結器、連結器和執行檔資訊、執行階段程式庫 (CRT)
contributed by <`RusselCK` >
###### tags: `RusselCK`
## [動態連結器篇](https://hackmd.io/@sysprog/c-dynamic-linkage?type=view#你所不知道的-C-語言:動態連結器篇)
“lingua franca” (IPA 音標 [ˌlɪŋgwə ˈfræŋkə]) 一詞源自 17 世紀義大利語稱呼「法蘭克語/口音」,後來引申為橋接用的語言
* 現代英語就扮演這樣的角色,讓世界各國、不同文化背景的人,得以透過共通的英語來交流。
* [How is English Used as a Lingua Franca Today?](https://www.altalang.com/beyond-words/how-is-english-used-as-a-lingua-franca-today/)
* 北京普通話 $\leftrightarrow$ 台灣國語 $\leftrightarrow$ 台語
* 而對近代程式語言來說,就是指 C 語言。
以 Java 程式語言來說,儘管有 Java 虛擬機器,甚至能用 Java 開發 Java 虛擬機器 (如 [Jikes RVM](http://www.jikesrvm.org/), [Maxine VM](https://en.wikipedia.org/wiki/Maxine_Virtual_Machine), [Graal VM](http://openjdk.java.net/projects/graal/)),但和作業系統相關的操作仍需要透過 C 語言 (或 C++),連同呼叫原本用 C/C++ 開發的函式庫在內。
:::warning
* [P-code 與 Kenneth Bowles 教授](https://www.facebook.com/JservFans/posts/1711808435612150)
* [UCSD Pascal pioneer Ken Bowles has died](https://news.ycombinator.com/item?id=18161217)
- 若 UCSD Pascal 裡頭的解譯 P-code 的直譯器,是用 C 語言開發的話,這個直譯器應該會用到 C 語言的函式庫,那麼,是否可與其他用 C 語言所開發的程式做連結,使用彼此的功能 ?
* 可以,但要先解決動態連結的問題
:::
### 用 `LD_PRELOAD` 做壞壞的事
#### 如何得知 malloc/free 的呼叫次數?
##### 簡單的作法 (使用 巨集)
```c
int malloc_count = 0, free_count = 0;
#define MALLOC(x) do { if (malloc(x)) malloc_count++; } while (0)
```
:::danger
1. 要改寫原始程式碼,將 `malloc` 換成 `MALLOC` 巨集
2. 對 C++ 不適用 (`new` 和 `delete`)
* 即便底層 [libstdc++](https://gcc.gnu.org/onlinedocs/libstdc++/) 也用 malloc()/free() 來實做 new 和 delete
3. 若使用到的函式庫 (靜態和動態) 裡頭若呼叫到 malloc()/free(),也無法追蹤到
:::
##### Interpositioning (使用 動態連結器 (dynamic linker))
* 以 GNU/Linux 搭配 [glibc](https://www.gnu.org/software/libc/) 為例,建立檔案 **malloc_count.c**
```clike
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <dlfcn.h>
void *malloc(size_t size) {
char buf[32];
static void *(*real_malloc)(size_t) = NULL;
if (real_malloc == NULL) {
real_malloc = dlsym(RTLD_NEXT, "malloc");
}
sprintf(buf, "malloc called, size = %zu\n", size);
write(2, buf, strlen(buf));
return real_malloc(size);
}
```
想要以新的 `malloc` 取代 原有的 `malloc`
:::info
* **`void *dlsym(void *handle, const char *symbol);`**
* obtain **address of a symbol** in a shared object or executable
* shared object : dll 檔, ...
* executable : 執行檔
* RTLD_NEXT
* 告知動態連結器,我們想從下一個載入的動態函式庫載入 malloc 的程式碼位址
:::
:::warning
為什麼 **malloc_count.c** 不需要 `void *p = dlopen(“libc.so.6”, RTLD_LAZY);` ?
* 因為 早就打開了
* **malloc_count.c** 是用 C 語言開發的,會需要 C 語言函式庫
同理,也不用主動關掉,因為是共用 `libc` 的
:::
* 編譯和執行:
```shell
$ gcc -D_GNU_SOURCE -shared -ldl -fPIC -o /tmp/libmcount.so malloc_count.c
$ LD_PRELOAD=/tmp/libmcount.so ls
```
即可得知每次 `malloc()` 呼叫對應的參數,甚至可以統計記憶體配置,完全不需要變更原始程式碼
:::info
* -shared
* -fPIC
* LD_PRELOAD
:::
:::success
【流程解析】
* 透過設定 LD_PRELOAD 環境變數,glibc 的 dynamic linker ([ld.so](http://man7.org/linux/man-pages/man8/ld.so.8.html)) 會在載入和重定位 (relocation) `libc.so` **之前**,載入我們撰寫的 `/tmp/libmcount.so` 動態連結函式庫
* 如此一來,我們實做的 malloc 就會在 `libc.so` 提供的 malloc 函式之前被載入。
* 當然,我們還是需要「真正的」 malloc,否則無法發揮作用,所以透過 dlsym 去從 `libc.so` 載入 malloc 程式碼
:::
* [glibc-abi](https://pagure.io/glibc-abi)
* [application binary interface, ABI](https://zh.wikipedia.org/wiki/应用二进制接口)
#### Unrandomize (暫時不「亂」的亂數)
- [ ] [Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs](https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/)
```c=
#include <stdlib.h>
#include <time.h>
int main(){
srand(time(NULL));
int i = 10;
while(i--) printf("%d\n",rand()%100);
return 0;
}
```
* 建立檔案 **unrandom.c**
```c
int rand(){
return 42; //the most random number in the universe
}
```
* 編譯和執行:
```shell
gcc -shared -fPIC unrandom.c -o unrandom.so
LD_PRELOAD=$PWD/unrandom.so ./random_nums
```
### Interpositioning 的其他應用
- 遊戲破解
- 執行時期追蹤
- sandboxing / software fault isolation (SFI)
- profiling
- 效能最佳化的函式庫 (如 [TCMalloc](http://goog-perftools.sourceforge.net/doc/tcmalloc.html))。
延伸閱讀:
* [Tutorial: Function Interposition in Linux](http://jayconrod.com/posts/23/tutorial-function-interposition-in-linux)
* [List of resources related to LD_PRELOAD](https://github.com/gaul/awesome-ld-preload)
也可使用 `_ld --wrap=symbol` 的方式,詳見_ [How to wrap a system call (libc function) in Linux](http://samanbarghi.com/blog/2014/09/05/how-to-wrap-a-system-call-libc-function-in-linux/)。
### Symbolism (what does `-Bsymbolic` do?)
- GNU ld 有個選項 `-Bsymbolic-functions` 會影響 LD_PRELOAD 的行為
>When creating a shared library, bind references to global function symbols to the definition within the shared library, if any. This option is only meaningful on ELF platforms which support shared libraries.
- [ ] [Symbolism and ELF files (or, what does -Bsymbolic do?)](https://blog.flameeyes.eu/2012/10/symbolism-and-elf-files-or-what-does-bsymbolic-do)
自己看
### ELF files ("No such file or directory" 可能跟你猜想的不一樣)
- [ ] [where is ELF interpreter](https://github.com/imay/imay.github.io/blob/master/_posts/2014-11-02-linker-loader.md)
- [ ] [PatchELF](https://nixos.org/patchelf.html)
==在動態連結的環境中,ELF interpreter 其實就是 dynamic linker!==
> Main binary specifies which loader to use
* linux 核心的程式碼 [fs/binfmt_elf.c](http://lxr.free-electrons.com/source/fs/binfmt_elf.c)
```c=2
/*
* linux/fs/binfmt_elf.c
*
* These are the functions used to load ELF format executables as used
* on SVr4 machines. Information on the format may be found in the book
* "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support
* Tools".
*
* Copyright 1993, 1994: Eric Youngdale (ericy@cais.com).
*/
```
```c=690
static int load_elf_binary(struct linux_binprm *bprm)
```
```clike=751
if (elf_ppnt->p_type == PT_INTERP) {
/* This is the program interpreter used for
* shared libraries - for now assume that this
* is an a.out format binary
*/
...
elf_interpreter = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL);
if (!elf_interpreter) goto out_free_ph;
retval = kernel_read(bprm->file, elf_ppnt->p_offset,
elf_interpreter, elf_ppnt->p_filesz);
```
program interpreter 找尋程式需要哪些共享的 libraries
:::info
* [UNIX System V](https://zh.wikipedia.org/wiki/UNIX_System_V)
* System V是AT&T的第一個**商業**UNIX版本的加強
:::
延伸閱讀:
- [ ] [Hacking Your ELF For Fun And Profit](http://mgalgs.github.io/2013/05/10/hacking-your-ELF-for-fun-and-profit.html)
- [ ] 《[Binary Hacks](https://ncku365-my.sharepoint.com/:b:/g/personal/p76091624_ncku_edu_tw/Ef-hwrKrKG5MvCwSIf688HUBiP3Ot2qzjzne0K1ttGH7BQ?e=o35u4p)》
* [ELF Hacks](https://maskray.me/blog/2015-03-26-elf-hacks)
### 複習 編譯器最佳化原理
:dart: [編譯器最佳化原理 筆記](https://hackmd.io/FspGCG57QfO5u6oaIYj0uQ?both#編譯器與最佳化原理、案例分析)
* Compilation units
* LTO (Link Time Optimization)
影片 1:16:00 ~ 1:37:00
### 《[Modern C](https://modernc.gforge.inria.fr/)》
* [C/C++ 中的 static, extern 的變數](https://medium.com/@alan81920/c-c-中的-static-extern-的變數-9b42d000688f)
Rule 摘錄
[ **Rule 4.22.2.1** ] File scope static const objects may be replicated in all compilation units that use them. (Page 169)
>英語中 replicate 有「複製」或「重複」的意思
==一旦物件宣告為 `static const`,那麼編譯器就可以施加更多樣的最佳化策略==
```c
static const x = 42;
// 編譯器可將程式裡的 `x` 全部換成 `42`
```
[ **Rule 4.22.2.2** ] File scope static const objects cannot be used inside inline functions with external linkage. Another way is to declare them
```clike
extern listElem const singleton;
```
and to define them in one of the compilation units:
```clike
listElem const singleton = { 0 };
```
This second method has the big **disadvantage** that **the value of the object is not available in the other units that include the declaration**. Therefore we may miss some opportunities for the compiler to optimize our code.
考慮以下程式碼:
```clike
inline listElem *listElem_init(listElem *el) {
if (el) *el = singleton;
return el;
}
```
如果編譯器已經得知 `singleton` 的內含值,那麼原本指定數值的操作就不用重複自記憶體載入,而且呼叫 `listElem_init()` 的地方就能更緊湊,對效能和程式追蹤有助益。
### Symbol Visibility
預設情況下,所有「不是 static」的 symbol (函式/變數) 都可會開放給其他 compilation unit 去存取,這樣的行為我們稱為 "**export**"。
* 一個 symbol 一旦 export,就可能遇到前述的 interpositioning,這很可能會導致非預期的行為。
解決方法是,妥善地設定 symbol visibility。
:::info
==gcc 和 clang 都支援 [visibility](https://gcc.gnu.org/wiki/Visibility) 屬性和 **`-fvisibility`** 編譯命令==,以便對每個 object file 來進行全域設定:
* **default** : 不修改 visibility
* **hidden** : 對 visibility 的影響與 **static** 這個 qualifier 相同。此 symbol 不會被放入 dynamic symbol table,其他動態連結函式庫或執行檔看不到此 symbol
:::
> [Standard Template Library, STL](https://zh.wikipedia.org/wiki/标准模板库)
> 《[The Annotated STL Source](http://read.pudn.com/downloads190/ebook/894697/STL-InsightByFU.pdf)》侯傑
範例 : 新酷音 [ [source](https://github.com/chewing/libchewing/blob/master/include/global.h) ]
```clike
if (__GNUC__ > 3) && (defined(__ELF__) || defined(__PIC__))
# define CHEWING_API __attribute__((__visibility__("default")))
# define CHEWING_PRIVATE __attribute__((__visibility__("hidden")))
#else
# define CHEWING_API
# define CHEWING_PRIVATE
#endif
```
* [C 语言中__attribute__的作用](https://winddoing.github.io/post/12087.html)
實驗:
- [ ] [Why symbol visibility is good](https://www.technovelty.org/code/why-symbol-visibility-is-good.html)
* 慮以下程式碼: (syms.c)
```clike
static int local(void) { }
int global(void) { }
int __attribute__((weak)) weak(void) { }
```
:::info
* **`attribute((weak))`**
* 若 linker 發先別人也有相同名稱的 Symbol,則會先用別人的
:::
* 編譯和分析: (Symbol table)
```shell
$ gcc -o syms.o -c syms.c
$ LC_ALL=C readelf --syms syms.so
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS syms.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 7 FUNC LOCAL DEFAULT 1 local
6: 0000000000000000 0 SECTION LOCAL DEFAULT 5
7: 0000000000000000 0 SECTION LOCAL DEFAULT 6
8: 0000000000000000 0 SECTION LOCAL DEFAULT 4
9: 0000000000000007 7 FUNC GLOBAL DEFAULT 1 global
10: 000000000000000e 7 FUNC WEAK DEFAULT 1
```
對照看之前的 malloc_count:
```shell
$ readelf --syms /tmp/libmcount.so | grep malloc
15: 00000000000007c0 163 FUNC GLOBAL DEFAULT 12 malloc
35: 0000000000000000 0 FILE LOCAL DEFAULT ABS malloc_count.c
36: 0000000000201050 8 OBJECT LOCAL DEFAULT 24 real_malloc.3854
61: 00000000000007c0 163 FUNC GLOBAL DEFAULT 12 malloc
```
* 修改 malloc_count.c,讓定義的程式碼變更為以下:
```clike
__attribute__((visibility("hidden"))) void *malloc(size_t size)
{ ... 其餘不變 ... }
```
就會發現 `LD_PRELOAD=/tmp/libmcount.so ls` 沒有效果。
換言之,我們定義的 `malloc` 已經變成 local,不會影響到其他動態連結函式庫和執行檔。
* 重新觀察:
```shell
$ readelf --syms /tmp/libmcount.so | grep malloc
35: 0000000000000000 0 FILE LOCAL DEFAULT ABS malloc_count.c
36: 0000000000201050 8 OBJECT LOCAL DEFAULT 24 real_malloc.3854
46: 00000000000007a0 163 FUNC LOCAL DEFAULT 12 malloc
```
可見到 visibility 從原本的 GLOBAL 變更為 LOCAL。
### 動態連結支援 (Dynamic Linking)
- [ ] [Linking](http://www.scs.stanford.edu/15wi-cs140/notes/linkers.pdf) (老師影片用的)
- [ ] [Linking](http://www.scs.stanford.edu/18wi-cs140/notes/linking.pdf) (new)
![](https://i.imgur.com/kL6uJkE.png)
* `f.o` 和 `c.o` 在 linking 會互相參照
![](https://i.imgur.com/SZmL379.png)
* 在動態連結,我們關注在 Compile time 之後的 Load/Run time
![](https://i.imgur.com/aJevea4.png)
* 此時並不知道 `printf` 的真實位址
* 因為尚未參照與 `printf` 有關的函式庫
![](https://i.imgur.com/mYBYuGD.png)
> 此時還是不知道 `printf` 的真實位址
![](https://i.imgur.com/pM84yO3.png)
* 連結參照到 動態連結函式庫、glibc 等之後,便可知道確切位置 (Relocation)
### Shared Libraries
* Make upgrading, bug fixing, and security patches easier
* Reduces total code size installed
* Plugins
![](https://i.imgur.com/8vmvH1X.png)
* `libc.a` 出現好多次
:::info
* [ar](https://linux.die.net/man/1/ar)
* create, modify, and extract from **archives**
* 裡面放 **relocatable object**
> `printf.o` 、`scanf.o` ...
:::
![](https://i.imgur.com/xBCjMtv.png)
:::info
* -fpic
* 允許在連結時期調整位址
![](https://i.imgur.com/GAZ515M.png)
:::
### 如何做到動態載入 Symbol ? (PLT、GOT)
![](https://i.imgur.com/nLzi2FZ.png)
* 查表 (PTL)
* 表裡面還有表 (GOT)
:::info
* **procedure Linkage table, PLT**
* entry for the function so that the dynamic loader can re-direct the function call.
* **global offset table, GOT**
* The PLT is a trampoline which gets the correct address of the function being called (from the _global offset table_, GOT) and bounces the function call to the right place.
![](https://i.imgur.com/lfKuo8e.png)
:::
![](https://i.imgur.com/59c2exc.png)
* 這裡的 `&dlfixup` 是假的位址 (Lazy Dynamic Linking)
* Program 載入後,**有需要才去找尋**真正的位址並更新
![](https://i.imgur.com/sGmj8uf.png)
後面還有 資安 的部分 ~
### 其他必看
- [ ] [Computer Science from the Bottom Up](https://www.bottomupcs.com/) ([Ian Wienand](https://github.com/ianw) 的電子書)
* [Chapter 9\. Dynamic Linking](https://www.bottomupcs.com/chapter08.xhtml)
- [ ] [C語言編程透視](https://github.com/tinyclub/open-c-book) (電子書)
* [Chap 4](https://github.com/tinyclub/open-c-book/blob/master/zh/chapters/02-chapter4.markdown)
- [ ] [Anatomy of Linux dynamic libraries](http://www.ibm.com/developerworks/library/l-dynamic-libraries/)
原共筆還有其他延伸閱讀及相關實際應用 ~
## [連結器和執行檔資訊](https://hackmd.io/@sysprog/c-linker-loader?type=view#你所不知道的-C-語言:連結器和執行檔資訊)
探討 gold (別懷疑,真的有一個 linker 名稱就叫做「金」) 如何讓 Linux 核心發揮 Link-Time Optimization (LTO) 效益,編譯出更精簡且更高效的 Linux 核心映像檔。
### 嵌入一個二進位檔案到執行檔
#### 範例 I :
假設有個二進位檔案名為 `blob`,可善用 `xxd` 工具:
```shell
$ xxd --include blob
```
:::info
* $ [xxd](https://linux.die.net/man/1/xxd)
* make a hexdump or do the reverse
* -i
* include
* output in C include file style
:::
為了解說方便,先製造一個檔案,紀錄長度:
```shell
$ uname -a > blob
$ uname -a | wc -c // 檢查字串長度 (bytes)
105
```
:::info
* $ [uname](https://linux.die.net/man/1/uname)
* print system information
* -a
* print all information
:::
* 將 `blob` 納入 ELF 檔案中:
```shell
$ ld -s -r -b binary -o blob.o blob
```
:::info
- [ ] [10分鐘讀懂 linker scripts](https://blog.louie.lu/2016/11/06/10%E5%88%86%E9%90%98%E8%AE%80%E6%87%82-linker-scripts/)
- [ ] [Linker Script 初探 - GNU Linker ld 手冊略讀](http://wen00072.github.io/blog/2014/03/14/study-on-the-linker-script/)
* $ [ld](https://linux.die.net/man/1/ld)
* The GNU linker
* ld combines a number of object and archive files, **relocates** their data and ties up symbol references.
* Usually the linker is invoked with at least one object file, but you can specify other forms of binary input files
* -b
* input-format
* 此例指定 二進位檔案 (binary) 為 input
* -o
* output
:::
觀察產生的 `blob.o` 有哪些 Symbol:
```shell
$ objdump -t blob.o
blob.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000069 g .data 0000000000000000 _binary_blob_end
0000000000000000 g .data 0000000000000000 _binary_blob_start
0000000000000069 g *ABS* 0000000000000000 _binary_blob_size
```
示範如何合併:
* 寫個測試程式 (`test.c`):
```clike=
#include <stdio.h>
int main(void) {
extern void *_binary_blob_start, *_binary_blob_end;
void *start = &_binary_blob_start, *end = &_binary_blob_end;
printf("Data: %p..%p (%zu bytes)\n",
start, end, end - start);
return 0;
}
```
:::warning
* `#2` : `_binary_blob_start` 、 `_binary_blob_end` 是 `blob.o` 裏頭的 Symbol,並不存在於 `test.c`,因此需要使用 `extern` 來宣告
:::
* 編譯、連結,和執行:
```shell
$ gcc test.c blob.o -o test
$ ./test
Data: 0x55ed5ed15010..0x55ed5ed15079 (105 bytes)
```
對照上面的 `105` bytes,符合。
:::info
* $ [strings](https://linux.die.net/man/1/strings)
* print the strings of printable characters in files.
:::
回頭看稍早產生的 `blob.o`:
```shell
$ readelf -S blob.o
There are 5 section headers, starting at offset 0x180:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .data PROGBITS 0000000000000000 00000040
0000000000000069 0000000000000000 WA 0 0 1
[ 2] .symtab SYMTAB 0000000000000000 000000b0
0000000000000078 0000000000000018 3 2 8
[ 3] .strtab STRTAB 0000000000000000 00000128
0000000000000037 0000000000000000 0 0 1
[ 4] .shstrtab STRTAB 0000000000000000 0000015f
0000000000000021 0000000000000000 0 0 1
```
:::info
* $ [readelf](https://linux.die.net/man/1/readelf)
* Displays information about ELF files.
* -S
* sections、section-headers
* Displays the information contained in the file's section headers
* 檔名前面有 `.` 代表 section
:::
#### 範例 II : ( [objcopy_to_carray](https://github.com/vogelchr/objcopy_to_carray) )
* 觀察 **Makefile**
```shell=20
passwd.o : /etc/passwd
objcopy -I binary $(OBJCOPY_ARCH) \
--rename-section .data=.rodata,alloc,load,readonly,data,contents \
--add-section ".note.GNU-stack"=/dev/null \
--set-section-flags ".note.GNU-stack"=contents,readonly \
$< $@ || (rm -f $@ ; exit 1)
```
複製 /etc/passwd 裡的內容,產生 **passwd.o**
:::info
* $ [objcopy](https://linux.die.net/man/1/objcopy)
* **copy** and translate **object files**
* objcopy uses the **GNU BFD** Library to read and write the object files.
:::
- [ ] [`readelf` vs. `objdump`: why are both needed](https://stackoverflow.com/questions/8979664/readelf-vs-objdump-why-are-both-needed)
* **testme.c**
```c=
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
extern void _binary__etc_passwd_size;
extern char _binary__etc_passwd_start;
extern char _binary__etc_passwd_end;
int
main(int argc, char **argv)
{
(void)argc;
(void)argv;
/* try one of the following two lines */
// size_t size = &_binary__etc_passwd_size;
size_t size = &_binary__etc_passwd_end - &_binary__etc_passwd_start;
printf("Dumping /etc/passwd, in memory @%p, size is %zu.\n",
&_binary__etc_passwd_start, size);
write(1,&_binary__etc_passwd_start,size); // 輸出內容
exit(0);
}
```
### Init hooks/script (允許特定程式碼在核心啟動早期就執行)
- [ ] [10分鐘讀懂 linker scripts](https://blog.louie.lu/2016/11/06/10%E5%88%86%E9%90%98%E8%AE%80%E6%87%82-linker-scripts/)
- [ ] [Linker Script 初探 - GNU Linker ld 手冊略讀](http://wen00072.github.io/blog/2014/03/14/study-on-the-linker-script/)
在 [F9 microkernel](https://github.com/f9micro/f9-kernel) 有個特徵 [Init hooks](https://github.com/f9micro/f9-kernel/blob/master/Documentation/init-hooks.txt),**允許特定程式碼在核心啟動早期就執行**
使用方式:
```clike
#include <init_hook.h>
#include <debug.h>
void hook_test(void) {
dbg_printf(DL_EMERG, "hook test\n");
}
INIT_HOOK(hook_test, INIT_LEVEL_PLATFORM - 1)
```
* 透過 GNU extension 去指定 ELF section: [include/init_hook.h](https://github.com/f9micro/f9-kernel/blob/master/include/init_hook.h):
```clike
#define INIT_HOOK(_hook, _level) \
const init_struct _init_struct_##_hook \
__attribute__((section(".init_hook"))) = { \
.level = _level, \
.hook = _hook, \
.hook_name = #_hook, \
};
```
將有用到 `INIT_HOOK` 的 Symbol (不論在何處使用),全部丟到 `.init_hook`
* 在 [platform/stm32f4/f9.ld](https://github.com/f9micro/f9-kernel/blob/master/platform/stm32f4/f9.ld) 配置了 `.init_hook` 的空間:
```clike
init_hook_start = .;
KEEP(*(.init_hook))
init_hook_end = .;
```
用 `init_hook_start`/`end` 包住`.init_hook` 的空間
* 最後在 [kernel/init.c](https://github.com/f9micro/f9-kernel/blob/master/kernel/init.c) 就清晰了:
```clike
extern const init_struct init_hook_start[];
extern const init_struct init_hook_end[];
static unsigned int last_level = 0;
int run_init_hook(unsigned int level) {
unsigned int max_called_level = last_level;
for (const init_struct *ptr = init_hook_start;
ptr != init_hook_end; ++ptr)
if ((ptr->level > last_level) &&
(ptr->level <= level)) {
max_called_level = MAX(max_called_level, ptr->level);
ptr->hook();
}
last_level = max_called_level;
return last_level;
}
```
### 連結器在軟體最佳化扮演重要角色
在**雲端運算** (伺服器超級多台) 中,每一個(小)程式都很重要,找不太到效能瓶頸
* Linker 的最佳化是個很好的切入點
* 程式最佳化程式
* 只改善 1% 就有很大的幫助
:dart: [Linker 筆記](https://hackmd.io/FspGCG57QfO5u6oaIYj0uQ#Linker)
* Linker 命令列 本身就是一種語言
* GNU ld linker
* Google gold linker
### 教材 : Computer Science from the Bottom Up
- [ ] [Computer Science from the Bottom Up](https://www.bottomupcs.com/) ([Ian Wienand](https://github.com/ianw) 的電子書)
* [Chapter 7. The Toolchain](https://www.bottomupcs.com/linker.xhtml) 術語解說
* [name decoration](https://zh.wikipedia.org/wiki/名字修饰) 符號 (Symbol) 處理
* [compilation example](https://www.bottomupcs.com/compilation_example.xhtml)
* UND : undefined
**hello.c**
```c
#include <stdio.h>
/* We need a prototype so the compiler knows what types function() takes */
int function(char *input);
/* Since this is static, we can define it in both hello.c and function.c */
static int i = 100;
/* This is a global variable */
int global = 10;
int main(void)
{
/* function() should return the value of global */
int ret = function("Hello, World!");
exit(ret);
}
```
**function.c**
```c
#include <stdio.h>
static int i = 100;
/* Declard as extern since defined in hello.c */
extern int global;
int function(char *input)
{
printf("%s\n", input);
return global;
}
```
Compiling
```shell
$ gcc -S hello.c
$ gcc -S function.c
```
Assemply
```shell
$ as -o function.o function.s
$ as -o hello.o hello.s
$ ls
function.c function.o function.s hello.c hello.o hello.s
```
```shell
$ readelf --symbols ./hello.o
Symbol table '.symtab' contains 15 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 4 OBJECT LOCAL DEFAULT 5 i
7: 0000000000000000 0 SECTION LOCAL DEFAULT 6
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
10: 0000000000000000 0 SECTION LOCAL DEFAULT 10
11: 0000000000000004 4 OBJECT GLOBAL DEFAULT 5 global
12: 0000000000000000 96 FUNC GLOBAL DEFAULT 1 main
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND function
14: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND exit
```
* 在 `hello.o` 中 `function` 為 undefined
```shell
$ readelf --symbols ./function.o
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS function.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 4 OBJECT LOCAL DEFAULT 5 i
7: 0000000000000000 0 SECTION LOCAL DEFAULT 6
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
10: 0000000000000000 0 SECTION LOCAL DEFAULT 10
11: 0000000000000000 128 FUNC GLOBAL DEFAULT 1 function
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND global
```
* 在 `function.o` 中 `function` 為 defined
Linking
:::info
we can spy on what gcc is doing under the hood with the `-v` (verbose) flag.
:::
```shell
/usr/lib/gcc-lib/ia64-linux/3.3.5/collect2 -static
/usr/lib/gcc-lib/ia64-linux/3.3.5/../../../crt1.o
/usr/lib/gcc-lib/ia64-linux/3.3.5/../../../crti.o
/usr/lib/gcc-lib/ia64-linux/3.3.5/crtbegin.o
-L/usr/lib/gcc-lib/ia64-linux/3.3.5
-L/usr/lib/gcc-lib/ia64-linux/3.3.5/../../..
hello.o
function.o
--start-group
-lgcc
-lgcc_eh
-lunwind
-lc
--end-group
/usr/lib/gcc-lib/ia64-linux/3.3.5/crtend.o
/usr/lib/gcc-lib/ia64-linux/3.3.5/../../../crtn.o
```
* crtbegin.o / crtend.o
* -lgcc / -gcc_eh (exception handling)
The first thing you notice is that a program called `collect2` is being called.
This is a simple wrapper around `ld` that is used internally by gcc.
### Startup Time Issue
- [ ] [Optimizing large applications](http://www.ucw.cz/~hubicka/slides/labs2013.pdf) (2013)
Firefox - Mozilla - gecko - [XUL:Lib XUL](https://wiki.mozilla.org/XUL:Lib_XUL)
- [ ] [使用 Prelink 加速程序啓動](https://www.twblogs.net/a/5c7d7b8ebd9eee114211f91d) (2004)
- [ ] .gnu.hash (2006)
* 原本的執行路徑
![](https://i.imgur.com/nueVA8l.png)
![](https://i.imgur.com/gjXPT07.png)
* 程式在執行之初,都在動態連結相關的 section 之間忙碌
* `.data.rel.ro` 和 `.rela.dyn`
- [ ] [elfhack](https://wiki.mozilla.org/Elfhack) (2010) 調整後
- [ ] [Improving libxul startup I/O by hacking the ELF format](https://glandium.org/blog/?p=1177)
![](https://i.imgur.com/9k8f43r.png)
* 程式執行之初,有機會執行程式內容相關的 section
* `.text`
- 20% of Firefox libxul image are relocations
- **relocation 耗時**
- ELF relocations are not terribly size optimized
- **relocation 占空間**
* REL relocations on x86 take 8 bytes
* RELA relocation on x86-64 take 24 bytes
- Elfhack compress the relocations
* ELFhack removes IP relative ELF relocations and store them in compact custom format. It handles well sequences of IP relative relocations in **vtables**.
* After ELF linking, ELFhack linking completes the process.
* ELFhack is general tool but not compatible with -z relro security feature.
- `7.5` MB of relocations → `0.3` MB.
- [ ] Feedback directed reordering (2013)
- [ ] [valgrind](https://zh.wikipedia.org/wiki/Valgrind)
- [ ] GCC FDO
![](https://i.imgur.com/JOGlITd.png)
- [ ] [AutoFDO](https://gcc.gnu.org/wiki/AutoFDO/Tutorial)
- [ ] [AutoFDO: Automatic feedback-directed optimization for warehouse-scale applications](https://static.googleusercontent.com/media/research.google.com/zh-TW//pubs/archive/45290.pdf)
### Linktime optimization (LTO)
* First released in GCC 4.5.
* 需要更多的 CPU 及 記憶體 資源
- 有機會產生更有效率的程式
![](https://i.imgur.com/qtxShw3.png)
- [ ] Linktime optimization in GCC (2014)
- [ ] [part 1 - brief history](http://hubicka.blogspot.com/2014/04/linktime-optimization-in-gcc-1-brief.html)
* [Tree-SSA](http://gcc.gnu.org/projects/tree-ssa/)
- [ ] [part 2 - Firefox](http://hubicka.blogspot.com/2014/04/linktime-optimization-in-gcc-2-firefox.html)
* 裡面有各種最佳化組合的比較圖表
- [ ] [part 3 - LibreOffice](http://hubicka.blogspot.com/2014/09/linktime-optimization-in-gcc-part-3.html)
* 裡面有各種最佳化組合的比較圖表
### [LLD - The LLVM Linker](http://lld.llvm.org)
* LLD is a drop-in replacement for the GNU linkers that accepts the same command line arguments and linker scripts as GNU.
- [ ] [What makes LLD so fast?](https://ncku365-my.sharepoint.com/:b:/g/personal/p76091624_ncku_edu_tw/EYeH1p1K7stPq_LixdYyH1MB_aWvA0ru9dBU_FIR3bVf8g?e=cAEXmk) / [WebM 錄影](https://video.fosdem.org/2019/K.4.201/llvm_lld.webm)
- [ ] [Linkers and Loaders](https://wh0rd.org/books/linkers-and-loaders/linkers_and_loaders.pdf)
* Peter Smith 宣稱 [lld](https://lld.llvm.org/) 比 GNU gold linker 快 2 到 3 倍,又比標準的 `ld.bfd` 快 5 到 10 倍
![](https://i.imgur.com/577GpRA.png)
### 延伸閱讀
- [ ] [The missing link: explaining ELF static linking, semantically](http://dominic-mulligan.co.uk/wp-content/uploads/2011/08/oopsla-elf-linking-2016.pdf)
> In the C programming language, a simple program such as 'hello, world!' exercises very few features of the language, and can be compiled even by a toy compiler. However, for a linker, even the smallest C program amounts to a complex job, since it links with the C library—one of the most complex libraries on the system, in terms of the linker features it exercises.
* formal proof
### 在 Linux 核心的應用案例
- [ ] [Shrinking the kernel with link-time garbage collection](https://lwn.net/Articles/741494/)
* 將沒有用到的 Symbol 去除
- [ ] [Shrinking the kernel with link-time optimization](https://lwn.net/Articles/744507/)
* [STM32](https://en.wikipedia.org/wiki/STM32)
* no MMU
- [ ] [STM32F429](http://wiki.csie.ncku.edu.tw/embedded/STM32F429)
* Dead-code elimination
* LTO and the kernel
- [ ] [Shrinking the kernel with an axe](https://lwn.net/Articles/746780/)
### 其他案例
* [tramp-test](https://github.com/ncultra/tramp-test)
```shell
$ ./dis.sh
$ cat tramp_test.o.asm
$ cat trampoline.o.asm
```
* [libelfmaster](https://github.com/elfmaster/libelfmaster): Secure ELF parsing/loading library for forensics reconstruction of malware, and robust reverse engineering tools
* [dt_infect](https://github.com/elfmaster/dt_infect): ELF Shared library injector using DT_NEEDED precedence infection. Acts as a permanent LD_PRELOAD
* [How the GNU C Library handles backward compatibility](https://developers.redhat.com/blog/2019/08/01/how-the-gnu-c-library-handles-backward-compatibility/)
## [執行階段程式庫 (CRT)](https://hackmd.io/@sysprog/c-runtime?type=view#你所不知道的-C-語言-執行階段程式庫-CRT)
你想過 C 程式 `int main(int argc, char *argv[])` 背後的運作原理嗎?
想過 C 程式既然「從 main() 開始執行」,那從哪裡獲取 argv[] 的內容呢?
以及最後 main 函式 return 數值時,又做了什麼處理,才能讓作業系統得知程式執行結果呢?
`atexit()` 一類的函式如何確保在 C 程式執行終止階段,得以執行註冊的函式?
:::info
- [ ] [使用 atexit() 讓程式被關閉時做對應的動作](https://kheresy.wordpress.com/2013/11/28/c-atexit/)
* [`int atexit(void (*function)(void));`](https://linux.die.net/man/3/atexit)
* register a function to be called at **normal process termination**
* i.e. `return 0;`
* Functions so registered are called in the reverse order of their registration; no arguments are passed.
:::
- [ ] [The Development of the C Language](https://www.bell-labs.com/usr/dmr/www/chist.html) C 語言標準化的緩慢過程
- [ ] [Re: [問卦] 寫程式語言的語言是怎麼來的?](https://disp.cc/b/163-aR41)
- [ ] [Re: [新聞] 她會個屁程式設計! 維密超模驚人簡歷](https://pttweb.tw/s/38ewP)
### 先看 Microsoft 的文件怎麼說
- [ ] [DLL 和 Visual C++ 執行階段程式庫行為](https://docs.microsoft.com/zh-tw/cpp/build/run-time-library-behavior?view=vs-2017)
* 指定使用動態連結程式庫 (DLL) 時,預設連結器就會包含 Visual C++ 執行階段程式庫 (**VCRuntime**)
* VCRuntime 包含初始化及終止 C/C++ 可執行檔所需的程式碼。
* VCRuntime 程式碼會提供內部 ==DLL 進入點函式呼叫==
* `_DllMainCRTStartup` 函式會執行基本工作,例如堆疊緩衝區安全性設定,C 執行階段程式庫 (CRT) 初始化及終止,而且會呼叫==建構函式和解構函式==
* `_DllMainCRTStartup` 也呼叫攔截函式的其他程式庫,例如 WinRT、 MFC 和 ATL 來執行他們自己的初始化及終止。而不需要這項初始化、 CRT 和其他程式庫,以及靜態變數,就會處於未初始化的狀態。
* `VCRuntime` 內部初始化和終止常式會呼叫是否以**靜態方式連結的 CRT** 或**動態連結的 CRTDLL**,會使用您的 DLL。
:::info
* [Constructors and Destructors in C++](https://www.studytonight.com/cpp/constructors-and-destructors-in-cpp.php)
* 在定義類別時,您可以使用建構函式(Constructor)來進行物件的初始化,而在物件釋放資源之前,您也可以使用「解構函式」 (Destructor)來進行一些善後的工作,例如清除動態配置的記憶體,或像是檔案的儲存、記錄檔的撰寫等等。
:::
- [ ] [How To Use the C Run-Time](https://support.microsoft.com/en-us/help/94248/how-to-use-the-c-run-time)
:::info
* [**`perror()`**](https://man7.org/linux/man-pages/man3/perror.3.html)
* print a system error message
* produces a message on standard error describing
the last error encountered during a call to a system or library function.
:::
- [ ] [MSVC與CRT的恩怨情仇](https://www.cnblogs.com/shijingjing07/p/5509640.html)
- [ ] [深入淺出 MFC](https://wizardforcel.gitbooks.io/jjhou-mfc/content/0.html) -侯傑
### 複習編譯流程
當你執行 `$ gcc -o hello hello.c` 時,會經歷以下步驟:
1. 前置處理 (pre-process) 給定的程式碼,移除程式內的註解,和其他技巧, 像是 expanding (展開) C 的 marco
2. 確認你的程式語法是否確實遵照 C/C++ 的規定,如果沒有符合的話,編譯器會出現警告
3. 將原始碼轉成組合語言 —— 它跟機器語言(machine code)非常相近,但仍在人類可理解的範圍內
4. 把組合語言轉成機器語言 —— 是的,這裡說的機器語言就是常提到的 bit 和 byte,也就是特定 0 和 1 的序列
5. 確認程式中用到的函式呼叫、全域變數是否正確,舉例來說:如若呼叫了不存在的函式,編譯器會顯示警告
6. 如果程式是由程式碼檔案來編譯,編譯器會整合起來
7. 編譯器會負責產生東西,讓系統上的 run-time loader 可以把程式載入記憶體內執行
8. 最後會把編譯完的執行檔存在指定的儲存空間
通常「編譯」(compilation) 是指上述第 1 到 4 個步驟,其他則稱為「連結」(linking),有時步驟 1 也指「前置處理」(pre-processing),而步驟 3 到步驟 4 則是「組譯」(assemble)。
![](https://i.imgur.com/Ic9vGte.png)
思考以下程式的執行: (**`hello.c`**)
```clike
int main() { return 1; }
```
* 編譯和執行
```shell
$ gcc -o hello hello.c
$ ./hello
```
看起來啥屁都沒發生 (或者說,看起來沒有 I/O)
* 利用 `echo $?` 來取得程式返回值
```shell
$ echo $?
1
```
這個 **`hello`** 已經執行完了,這個回傳值是如何被保留下來的?
程式結束後, CRT 會自動執行 `exit()`,`return` 的 `1` 即為 **exit status**
:::info
* [**`void _exit(int status);`**](https://man7.org/linux/man-pages/man2/exit.2.html)
* terminate the calling process
* These functions do not return.
* The value `status & 0xFF` is returned to the parent process as the **process's exit status**, ...
:::
:::warning
* 程式出錯時,我們可以利用 `atexit()` 來檢查問題是否出在所使用的函式上
:::
### 為何需要 C runtime ?
C 語言和一般的程式語言有個很重要的差異,就是 C 語言設計來滿足系統程式的需求
* 作業系統核心
* 一系列的工具程式,像是 ls, find, grep 等等
* 如果忽略指標操作這類幾乎可以直接對應於組合語言的指令的特色
C 語言之所以需要 runtime,有以下幾個地方:
1. `int main() { return 1; }` 也就是 `main()` 結束後,**要將 exit code 傳遞給作業系統的程式**,這部份會放在 `crt0`
2. **exception handling**,不要懷疑,C 語言當然有這個特徵,只是透過 `setjmp` 和 `longjmp` 函式來存取,這需要額外的函式庫 (如 libgcc) 來協助處理 stack
3. **算術操作**,特別在硬體沒有 **FPU** 時,需要 [libgcc](https://gcc.gnu.org/onlinedocs/gccint/Libgcc.html) 來提供浮點運算協助
> - [ ] [What's the difference between hard and soft floating point numbers?](https://stackoverflow.com/questions/3321468/whats-the-difference-between-hard-and-soft-floating-point-numbers)
> - [ ] [ArmHardFloatPort](https://wiki.debian.org/ArmHardFloatPort/VfpComparison) 指出在 gcc 針對 Arm 平台上有三種浮點數處理機制,也就是參數 `-mfloat-abi` 的選項有:
> * **`soft`** - this is pure software
> * **`softfp`** - this supports a hardware FPU, but the ABI is soft compatible.
> * **`hard`** - the ABI uses float or VFP registers.
### 驗證執行檔
* 在核心裡的「驗證執行檔」步驟,是要驗證什麼?
* UNIX 的「執行檔」有很多種可能,一個是依據特定格式保存的機械碼,也可能是透過額外程式去解析的 shell script,作業系統核心必須得事先解析並確認這個合法的執行檔,才能著手去執行
* 近來還有對執行檔進行簽章的機制,請見: [ELF executable signing and verification ](https://lwn.net/Articles/532710/)
* secureboot mode
* signelf
* opensll
### `int main(int argc, char *argv[])` 背後的學問
:::danger
有些書上使用 `void main()` 的函式宣告,這是錯誤的。
:::
C++ 之父 Bjarne Stroustrup 在他的 [C++ Style and Technique FAQ](http://www.stroustrup.com/bs_faq2.html#void-main) 中明確地寫著
> "The definition void main( ) { /* … */ } is not and never has been C++, nor has it even been C."
C 語言規範 5.1.2.2.1 :
> It shall be defined with **a return type of int** and with **no parameters** or with **two parameters (argc and argv)**
複習名詞:
![](http://i.imgur.com/K2XsAUi.png)
* "argument" 的重點是「傳遞給函式的形式」
> expressions passed into a function
* 稱 `argc` 是 argument count
* 稱 `argv` 是 argument vector
:::info
* [**`write()`**](https://www.man7.org/linux/man-pages/man2/write.2.html)
* [**`writev()`**](https://linux.die.net/man/2/writev)
* write data into multiple buffers
:::
- "parameter" 的重點是「接受到的數值」
> values received by the function
比方說 C++ 有 [parameterized type](https://isocpp.org/wiki/faq/templates#param-types),就是說某個型態可以當作另外一個型態的「參數」,換個角度說,「型態」變成像是數值一樣的參數。
* Caller
> 呼叫者(通常就是在其他函式呼叫的function)
- Callee
> 被呼叫者(被呼叫的 function)
- [ ] [Parameter (computer programming)](https://en.wikipedia.org/wiki/Parameter_(computer_programming))
### 程式使用函式呼叫在組合語言的實作
我們看到包含 **`envp`** 的宣告:
```cpp
int main(int argc, char *argv[], char *envp[])
{ ... }
```
- [ ] [(六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制](https://www.cnblogs.com/0xcafebabe/p/4434218.html)
* 故意改寫為以下: (**`x.c`**)
```cpp
#include <stdio.h>
int main(int argc, char (*argv)[0])
{
puts(((char **) argv)[0]);
return 0;
}
```
* 使用 gdb 觀察:
```shell
$ gcc -o x x.c -g
$ gdb -q x
```
```shell
(gdb) b main
(gdb) r
(gdb) print *((char **) argv)
$1 = 0x7fffffffe7c9 "/tmp/x"
```
這裡符合預期,但接下來:
```shell
(gdb) x/4s (char **) argv
0x7fffffffe558: "\311\347\377\377\377\177"
0x7fffffffe55f: ""
0x7fffffffe560: ""
0x7fffffffe561: ""
```
```shell
(gdb) x/4s (argv)
0x7fffffffe558: "\311\347\377\377\377\177"
0x7fffffffe55f: ""
0x7fffffffe560: ""
0x7fffffffe561: ""
```
看不懂了,要換個方式:
```shell
(gdb) x/4s ((char **) argv)[0]
0x7fffffffe7c9: "/tmp/x"
0x7fffffffe7d0: "LC_PAPER=zh_TW"
0x7fffffffe7df: "XDG_SESSION_ID=91"
0x7fffffffe7f1: "LC_ADDRESS=zh_TW"
```
![](https://i.imgur.com/QndglVq.png)
原來後 3 項是 envp (environment variables),在 C run-time 傳遞進來的內容和 `printenv` 輸出一致
```shell
(gdb) shell printenv
```
**下面從影片 1:35:00 開始~**
假設 PID = 31114,那麼我們可觀察:
```shell
cat /proc/31114/cmdline
cat /proc/31114/environ
```
:::info
讀取 `argv[0]` 和 `cmdline` 來判斷執行的程式名稱,有個非常巧妙的應用:
- [ ] [BusyBox - The Swiss Army Knife of Embedded Linux](https://busybox.net/downloads/BusyBox.html)
:::
- [ ] [深度剖析 C 語言 main 函式](https://blog.csdn.net/z_ryan/article/details/80985101)
* `_start()`
* `__attribute__()`
### GNU Toolchain
![](https://i.imgur.com/jDdgIaH.png)
* gcc : GNU compiler **collection**
* as : GNU assembler
* ld : GNU linker
* gdb : GNU debugger
(過度精簡的) 編譯的流程還有格式
![](http://i.imgur.com/9c0k1v0.png)
.coff 和 .elf 分別表示以下:
* [COFF (common object file format)](https://en.wikipedia.org/wiki/COFF) : 是種用於執行檔、目的碼、共享函式庫 (shared library) 的檔案格式
* [ELF (extended linker format)](https://en.wikipedia.org/wiki/Elf) : GNU/Linux 和 *BSD 上最常用的執行檔格式,用於執行檔、目的碼、共享函式庫和核心的標準檔案格式,**用來取代 COFF**
- [ ] [Computer Science from the Bottom Up](https://www.bottomupcs.com/)
- [ ] [Starting a process](https://www.bottomupcs.com/starting_a_process.xhtml)
* `__libc_csu_init` / `__libc_csu_fini`
### 隱藏的 `crt0`
[crt0](https://en.wikipedia.org/wiki/Crt0) (也稱為 `c0`)
```=
.text
.globl _start
_start: # _start is the entry point known to the linker
mov %rsp, %rbp # setup a new stack frame
mov 0(%rbp), %rdi # get argc from the stack
lea 8(%rbp), %rsi # get argv from the stack
call main # %rdi, %rsi are the first two args to main
mov %rax, %rdi # mov the return of main to the first argument
call exit # terminate the program
```
* `#6` : 將 `argc`、`argv[]`存入 stack
* `#11` : 將 `main()` 的回傳值放入 `%rax`
* `#12` : call `exit`
[Newlib](https://en.wikipedia.org/wiki/Newlib)
[newlib/i386 的實作程式碼](https://github.com/eblot/newlib/blob/master/newlib/libc/sys/linux/machine/i386/crt0.c)
```C
extern char **environ;
extern int main(int argc, char **argv, char **envp);
extern char _end;
extern char __bss_start;
void _start(int args) {
/*
* The argument block begins above the current stack frame, because we
* have no return address. The calculation assumes that sizeof(int) ==
* sizeof(void *). This is okay for i386 user space, but may be invalid in
* other cases.
*/
int *params = &args - 1;
int argc = *params;
char **argv = (char **) (params + 1);
environ = argv + argc + 1;
/* Note: do not clear the .bss section. When running with shared
* libraries, certain data items such __mb_cur_max or environ
* may get placed in the .bss, even though they are initialized
* to non-zero values. Clearing the .bss will end up zeroing
* out their initial values.
*/
tzset(); /* initialize timezone info */
exit(main(argc, argv, environ));
}
```
延伸閱讀:
* [Creating a C Library](https://wiki.osdev.org/Creating_a_C_Library)