LSA 讀書會 Tarball === # 什麼是執行檔? 執行檔常見的英文名稱會是 "executable file", "executable" 或是 "binary",可以讓電腦「編碼過的指令(instructions)來執行指定的任務」,該檔案就稱為執行檔。 - 那 `.sh` shell scripts(~~腳本~~,指令碼)是執行檔嗎? - 不是! - 那 `.py` 是執行檔嗎? - 不是! 那什麼才是? 在 Linux 中,使用 file 指令看到 [ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) 的才是。 ![](https://s3.us-west-2.amazonaws.com/secure.notion-static.com/fd9e0d5f-fbee-481f-9553-f745baa9357f/Untitled.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAT73L2G45O3KS52Y5%2F20210424%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20210424T130037Z&X-Amz-Expires=86400&X-Amz-Signature=e2d9ae386efe9517f454788136e0a937be72efd0d0ff91acf54e4a6f30d55bf3&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22Untitled.png%22) [常見的執行檔格式](https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats): - ELF - COM - EXE > 延伸閱讀:為何 file 指令知道檔案格式? - [Working with Magic numbers in Linux](https://www.geeksforgeeks.org/working-with-magic-numbers-in-linux/) - [List of file signatures](https://en.wikipedia.org/wiki/List_of_file_signatures) - [magic - Format of the /etc/magic file](https://www.ibm.com/docs/en/zos/2.4.0?topic=formats-magic-format-etcmagic-file) # 程式怎麼被編譯成執行檔? ![](https://s3.us-west-2.amazonaws.com/secure.notion-static.com/2f664d0c-29cb-4b71-8bed-34ce65fced93/Untitled.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAT73L2G45O3KS52Y5%2F20210424%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20210424T130138Z&X-Amz-Expires=86400&X-Amz-Signature=97d602297eb5b85abbeb2ff032958ebba4423ca1bdea76fa1743d05f31810c43&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22Untitled.png%22) 幫大家複習一下: - Assembly code - 非常接近 instructions 的低階程式語言 - Object code - 已經算是 machine code 了,只是會被包裝成各種格式的 object file - 通常是可以重新定址([relocatable](https://en.wikipedia.org/wiki/Relocation_(computing))) - object file 含有 metadata 資訊,以利連結、偵錯 - 運作方式有點像是 [shared library](https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries) - 在 Linux 下副檔名通常是 `.o`,Windows 下通常是 `.obj` 詳情請洽系統程式相關書籍 - [系統程式 -- 陳鍾誠](https://github.com/cccbook/sp/wiki/spbook?fbclid=IwAR3ZyPcAcKzMC87OVYgP4UMWP0bKRX1yGsLfSOK1IPa2KieocZtREnguFnc)(免費電子書) - [著名的貝殼書](https://www.amazon.com/-/zh_TW/dp/0201423006/ref=sr_1_1?dchild=1&keywords=System+Software%3A+An+Introduction+to+Systems+Programming+Leland&qid=1619163215&s=digital-text&sr=1-1) # C 語言函式庫只有一種嗎? 不,除了在 Linux 發行版中常見到 [GNU C Library (glibc)](https://en.wikipedia.org/wiki/GNU_C_Library),常見的輕量化系統 [alpine](https://alpinelinux.org/) 內建的是 [musl libc](http://musl.libc.org/) 輕量化 C 函式庫,微軟自己有實作 [Visual C++ runtime library](https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B)。 只要是照著 C 語言標準[實作](https://en.wikipedia.org/wiki/C_standard_library#Implementations)的,都可以叫做 C 標準函式庫,只是有些特性不同(如記憶體管理機制),~~造成逆向工程時有有點麻煩~~。 # Obj 檔編譯方式 ```c ##### a.c ##### #include <stdio.h> int main(){ puts("expecto"); pp(); return 0; } ##### lib.c ##### #include <stdio.h> void pp() { puts("patronum"); } ``` 同時編譯 ```bash $ ls a.c lib.c $ gcc -c a.c lib.c $ ls a.c a.o lib.c lib.o $ gcc a.o lib.o $ ./a.out expecto patronum ``` 指定函式庫 ```bash $ gcc -c lib.c $ ls a.c lib.c lib.o $ gcc -lm lib.o a.c ``` # Makefile 的使用 Make 為一從原始碼自動化建置(build)執行檔、函式庫的工具,執行時會讀取其設定檔 Makefile,並且依照裡面的指引去進行相關操作。 其會檢查檔案的依賴關係,透過減少重複的編譯連結任務,即可加快整體建置時間;也可以偵測當前系統環境以做出相對應的建置調整。 欲了解大型程式碼專案開發時,先了解其[構建相關設定](https://michaelchen.tech/applied-c-programming/build-automation-system/)檔如 Makefile、CMake、Bazel、Ninja 等,對於了解專案架構會有不錯的幫助。 詳細使用請查看:[https://hackmd.io/@sysprog/SySTMXPvl](https://hackmd.io/@sysprog/SySTMXPvl) # 什麼是 tarball 套件? tar 是用來打包的指令,使用 tar 打包完的那個檔案叫做 tarball,裝了程式碼、README、Makefile 等安裝所需檔案的 tarball 叫做 tarball 套件。 在安裝、更新套件時,常見有兩種方式: - 可以下載、跟隔壁鄰居要已經編譯好的執行檔 - 透過 `apt install` 都是安裝別人編譯好的 - 也可以自己下載程式原始碼來編譯 - 不怕執行檔被加料 - 但需要較久時間、也需要有對應的編譯工具 > 編按:除了用 tar 打包,在 Git 環境下也可以用 `git archive` 哦 # Tarball 的常見使用方式 **安裝基礎操作流程:** 1. 取得原始檔:將 tarball 檔案在 `/usr/local/src` 目錄下解壓縮 2. 取得步驟流程:進入新建立的目錄底下,去查閱 INSTALL 與 README 等相關檔案內容 3. 相依屬性軟體安裝:根據 INSTALL/README 的內容察看並安裝好一些相依的軟體 (非必要); 4. 建立 makefile:以自動偵測程式 (configure 或 config) 偵測作業環境,並建立 Makefile 這個檔案 5. 編譯:以 make 這個程式並使用該目錄下的 Makefile 做為他的參數設定檔,來進行 make (編譯或其他) 的動作; 6. 安裝:以 make 這個程式,並以 Makefile 這個參數設定檔,依據 install 這個標的 (target) 的指定來安裝到正確的路徑! **指令下達方式:** 1. `./configure` 這個步驟就是在建立 Makefile 這個檔案囉!通常程式開發者會寫一支 scripts 來檢查你的 Linux 系統、相關的軟體屬性等等,這個步驟相當的重要, 因為未來你的安裝資訊都是這一步驟內完成的!另外,這個步驟的相關資訊應該要參考一下該目錄下的 README 或 INSTALL 相關的檔案! - `--help` 查看說明 - `--prefix=` 更改安裝路徑,預設為 `/usr/local` - [其他參數請查看官方手冊](https://www.gnu.org/prep/standards/html_node/Directory-Variables.html#Directory-Variables) 2. **`make clean`** make 會讀取 Makefile 中關於 clean 的工作。這個步驟不一定會有,但是希望執行一下,因為他可以去除目標檔案!因為誰也不確定原始碼裡面到底有沒有包含上次編譯過的目標檔案 (*.o) 存在,所以當然還是清除一下比較妥當的。 至少等一下新編譯出來的執行檔我們可以確定是使用自己的機器所編譯完成的嘛! 3. **`make`** make 會依據 Makefile 當中的預設工作進行編譯的行為!編譯的工作主要是進行 gcc 來將原始碼編譯成為可以被執行的 object files ,但是這些 object files 通常還需要一些函式庫之類的 link 後,才能產生一個完整的執行檔!使用 make 就是要將原始碼編譯成為可以被執行的可執行檔,而這個可執行檔會放置在目前所在的目錄之下, 尚未被安裝到預定安裝的目錄中; 4. **`make install`** 通常這就是最後的安裝步驟了,make 會依據 Makefile 這個檔案裡面關於 install 的項目,將上一個步驟所編譯完成的資料給他安裝到預定的目錄中,就完成安裝啦! > 編按:如果你在軟體中看到有 `configure` 檔案,執行後會生成 Makefile,那 9 成是使用 [GNU Autotools (aka GNU Build System)](https://en.wikipedia.org/wiki/GNU_Autotools) 管理。 如果安裝的目錄非預設,那可自定義 man page 檔案搜尋路徑:(for Debian, CentOS) ```bash export MANPATH=":/path/to/custom/man" ``` ~~DEMO TIME for NTP installation~~ # 利用 patch 更新原始碼 ```bash diff --Naur old_file new_file > my_patch-1 # 修補檔案 patch < my_patch-1 # 取消修補 patch -R < my_patch-1 ``` > 補充:git 中可以使用 format-patch 來達到類似的功能 ~~ DEMO TIME for NTP installation ~~ # ldconfig 與 /etc/ld.so.conf 若想要增加軟體的開啟速度,可以將其所需的動態函式庫預先載入到記憶體當中,這時候就是透過 ldconfig 啦! 步驟: 1. 在 /etc/ld.so.conf 裡面寫下「想要讀入快取記憶體當中的動態函式庫所在的**目錄**」 2. 利用 ldconfig 這個執行檔將 /etc/ld.so.conf 的資料讀入快取當中 3. 同時也將資料記錄一份在 /etc/ld.so.cache 這個檔案當中 查看快取中有哪些動態連結函式庫: ```bash ldconfig -p $ ldconfig -p 1471 libs found in cache `/etc/ld.so.cache' libz3.so.4 (libc6,x86-64) => /lib/x86_64-linux-gnu/libz3.so.4 libz3.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libz3.so libzvbi.so.0 (libc6,x86-64) => /lib/x86_64-linux-gnu/libzvbi.so.0 ... ``` 查看某軟體所需的動態連結函式庫有哪些: ```bash ldd YOUR_EXE $ ldd /bin/ls linux-vdso.so.1 (0x00007ffff7fcd000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007ffff7f5a000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7d68000) libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007ffff7cd8000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ffff7cd2000) /lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcf000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ffff7caf000) ```