修改 Makefile

已經把程式分成數個檔案了,接著,也該來跟著改 Makefile 了。底下的 Makefile,會把現在所在資料夾的所有 .c 檔全部編譯並連結起來,做出 9cc 這個可執行檔。假設專案的標頭檔只有 9cc.h 這1個檔案,而這個標頭檔會被所有的 .c 檔所引入。

Makefile
CFLAGS=-std=c11 -g -static
SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o)

9cc: $(OBJS)
        $(CC) -o 9cc $(OBJS) $(LDFLAGS)

$(OBJS): 9cc.h

test: 9cc
        ./test.sh

clean:
        rm -f 9cc *.o *~ tmp*

.PHONY: test clean

注意 Makefile 的縮排必須要用 tab 而不能用空白。

make是非常強力的工具,精通不是必要的,但是熟悉到可以讀懂上面的 Makefile 的程度的話,在很多地方都能派上用場。我們在此針對上面的 Makefile 進行說明。

Makefile 中,1條規則是由以冒號分隔的行,和以 tab 縮排的0行以上的指令所構成。冒號前的名字稱為「目標」(target)。冒號後面0個以上的檔案名稱被稱為相依檔案。

執行make foomake就會試著做出 foo 的檔案。如果所指定的目標檔案已經存在的話,如果目標檔案比相依檔案舊的話,make就會重新執行該條目標的規則。因此,就可以只在程式碼有變更的時候才進行重編。

.PHONY代表不是真正的目標的特殊名字。make testmake clean並不是為了做出 test 或 clean 的檔案而執行的,但make並不知道這件事,如果剛好有 test 或 clean 的檔案在的話,make testmake clean就不會做任何事了。只要在在.PHONY裡指定像這樣的假目標,就不會真的想要做出該檔案,告訴make說:不論是否存在該目標的檔案都要執行該目標的規則。

CFLAGSSRCSOBJS為變數。

CFLAGSmake內嵌的規則中會讀取的變數,是用來寫要給C編譯器下的指令參數。此處我們給出以下幾個 flags:

  • -std=c11: 告訴編譯器我們的程式碼是用C最新的標準C11寫的

  • -g: 輸出除錯資訊

  • -static: 靜態連結

SRCS的右側使用的wildcardmake所提供的函式,會把和其引數符合的檔名展開。$(wildcard *.c)在現階段,會展開為main.c parse.c codegen.c container.c

OBJS的右側使用變數的替換規則,生成把SRCS中的.c換成.o的值。SRCSmain.c parse.c codegen.c container.c,所以OBJS會是main.o parse.o codegen.o container.o

有這些認識之後,我們來追看看執行make 9cc會發生什麼事。make會想要生成引數所指定的目標,所以生出 9cc 這個檔案就是指令的最終目標(如果沒有引數就會是最初的目標,所以在此處不指定9cc也沒關係)。make會確認相依性,看有沒有少檔案,或是檔案太舊都會執行指令編出檔案。

9cc相依的檔案為當下資料夾內 .c 檔所對應的 .o 檔。如果上次make執行後有留下 .o 檔,並且其時間戳記(timestamp)比 .c 檔新,make就不會多此一舉重新執行一樣的指令。只有在 .o 不存在,或是 .c 檔比較新的時候,編譯器才會執行產生 .o 檔。

$(OBJS): 9cc.h這條規則表示所有的 .o 檔案都相依於 9cc.h。所以如果 9cc.h 有變更,所有的 .o 檔都會被重編。

小知識:用編譯器製作後門

有種把系統裡的編譯器換成惡意的編譯器,用其編譯出來的程式會被擅自埋進惡意程式碼的攻擊方式。

舉例來說,螢幕保護程式本身是寫成以全螢幕顯示,直到輸入正確格式的密碼才會結束的程式。我們來想想要怎麼破解它。

假定攻擊者有編譯器和螢幕保護程式的原始程式碼。於是攻擊者就可以改造編譯器,判斷輸入的檔案和螢幕保護程式是否一致,當一致的時候就追加上特別的操作。

螢幕保護程式中,應該可以找到檢查輸入的密碼的程式碼。我們來想像一下,當編譯含有這段程式碼的檔案的時候,編譯器會輸出會擅自讓攻擊者的密碼通過的程式。如此,使用該編譯器編譯出來的螢幕保護程式,不只知道密碼的合法使用者,連攻擊者也可以成功解鎖。

像這樣的攻擊,再怎麼檢查螢幕保護程式的原始程式碼也檢查不出後門。因為後門並不是在那邊,而是藏在編譯器的二進制檔案裡。

這類攻擊不僅限於編譯器。例如針對 OS 做修改,我們可以想像,可以只在特定使用者執行螢幕保護程式的時候,默默執行別的惡意的螢幕保護程式的 OS。為了妨礙檢查,修改objdump或除錯器、OS 的檔案 IO,以達到隱藏後門的精巧架構也是可以想像的。

總而言之,軟體這種東西,除非使用可信賴的程式來製作和執行,都無法真正被相信。

Last updated