XX的(de)學習日記(嵌入式)三——編譯器背後的(de)故事
發表時(shí)間:2020-10-19
發布人(rén):融晨科技
浏覽次數:47
編譯器背後的(de)故事
- 一、可執行程序是(shì)如何被組裝的(de)?
- 1. 用 gcc 生成 .a 靜态庫和(hé / huò) .so 動态庫
- 1.1 編輯得到(dào)舉例的(de)程序:hello.h、hello.c和(hé / huò)main.c
- 1.2 将hello.c編譯成.o文件;
- 1.3由.o文件創建靜态庫
- 1.4 在(zài)程序中使用靜态庫
- 1.5由.o文件創建動态庫文件
- 1.6 在(zài)程序中使用動态庫
- 2.Linux下靜态庫.a與.so庫文件的(de)生成與使用
- 2.1 編輯所需程序
- 2.1 靜态庫.a文件的(de)生成與使用
- 2.2 共享庫.so文件的(de)生成與使用
- 3. 将第一次學習内容的(de)程序進行改編
- 3.1 完成目标(1)
- 3.2 完成目标(2)
- 二、Gcc不(bù)是(shì)一個(gè)人(rén)在(zài)戰鬥。
- 1. Linux GCC常用命令
- 1.1 程序準備
- 1.2 程序的(de)簡單編譯
- 1.3 程序的(de)分步編譯
- 2. 比較hello.asm與C代碼生成的(de)可執行文件
- 2.1 搭建環境:在(zài)Ubuntu上(shàng)安裝nasm
一、可執行程序是(shì)如何被組裝的(de)?
1. 用 gcc 生成 .a 靜态庫和(hé / huò) .so 動态庫
我們通常把一些公用函數制作成函數庫,供其它程序使用。函數庫分爲(wéi / wèi)靜态庫和(hé / huò)動态庫兩種。靜态庫在(zài)程序編譯時(shí)會被連接到(dào)目标代碼中,程序運行時(shí)将不(bù)再需要(yào / yāo)該靜态庫。動态庫在(zài)程序編譯時(shí)并不(bù)會被連接到(dào)目标代碼中,而(ér)是(shì)在(zài)程序運行是(shì)才被載入,因此在(zài)程序運行時(shí)還需要(yào / yāo)動态庫存在(zài)。
我們通過舉例來(lái)說(shuō)明在(zài)Linux中如何創建靜态庫和(hé / huò)動态庫,以(yǐ)及使用它們,在(zài)創建函數庫前,我們先來(lái)準備舉例用的(de)源程序,并将函數庫的(de)源程序編譯成.o文件。
1.1 編輯得到(dào)舉例的(de)程序:hello.h、hello.c和(hé / huò)main.c
hello.c是(shì)函數庫的(de)源程序,其中包含公用函數hello,該函數将在(zài)屏幕上(shàng)輸出(chū)"Hello XXX!"。hello.h爲(wéi / wèi)該函數庫的(de)頭文件。main.c爲(wéi / wèi)測試庫文件的(de)主程序,在(zài)主程序中調用了(le/liǎo)公用函數hello。
hello.h
hello.c
main.c
1.2 将hello.c編譯成.o文件;
無論靜态庫,還是(shì)動态庫,都是(shì)由.o文件創建的(de)。因此,我們必須将源程序hello.c通過gcc先編譯成.o文件。
在(zài)系統提示符下鍵入以(yǐ)下命令得到(dào)hello.o文件。
gcc -c hello.c
通過ls命令查看是(shì)否生成了(le/liǎo)hello.o文件
1.3由.o文件創建靜态庫
靜态庫文件名的(de)命名規範是(shì)以(yǐ)lib爲(wéi / wèi)前綴,緊接着跟靜态庫名,擴展名爲(wéi / wèi).a。例如:我們将創建的(de)靜态庫名爲(wéi / wèi)myhello,則靜态庫文件名就(jiù)是(shì)libmyhello.a。在(zài)創建和(hé / huò)使用靜态庫時(shí),需要(yào / yāo)注意這(zhè)點。創建靜态庫用ar命令。
在(zài)系統提示符下鍵入以(yǐ)下命令将創建靜态庫文件libmyhello.a,并用ls命令查看結果
ar -crv libmyhello.a hello.o
1.4 在(zài)程序中使用靜态庫
靜态庫制作完了(le/liǎo),如何使用它内部的(de)函數呢?隻需要(yào / yāo)在(zài)使用到(dào)這(zhè)些公用函數的(de)源程序中包含這(zhè)些公用函數的(de)原型聲明,然後在(zài)用gcc命令生成目标文件時(shí)指明靜态庫名,gcc将會從靜态庫中将公用函數連接到(dào)目标文件中。注意,gcc會在(zài)靜态庫名前加上(shàng)前綴lib,然後追加擴展名.a得到(dào)的(de)靜态庫文件名來(lái)查找靜态庫文件。
在(zài)程序3:main.c中,我們包含了(le/liǎo)靜态庫的(de)頭文件hello.h,然後在(zài)主程序main中直接調用公用函數hello。下面先生成目标程序hello,然後運行hello程序看看結果如何。
我們輸入以(yǐ)下指令:gcc main.c libmyhello.a -o hello
我們删除靜态庫文件試試公用函數hello是(shì)否真的(de)連接到(dào)目标文件 hello中了(le/liǎo)。
(這(zhè)裏我打錯了(le/liǎo)一段指令,馬了(le/liǎo)别介意: D)
程序照常運行,靜态庫中的(de)公用函數已經連接到(dào)目标文件中了(le/liǎo)。
1.5由.o文件創建動态庫文件
我們繼續看看如何在(zài)Linux中創建動态庫。我們還是(shì)從.o文件開始。
動态庫文件名命名規範和(hé / huò)靜态庫文件名命名規範類似,也(yě)是(shì)在(zài)動态庫名增加前綴lib,但其文件擴展名爲(wéi / wèi).so。例如:我們将創建的(de)動态庫名爲(wéi / wèi)myhello,則動态庫文件名就(jiù)是(shì)libmyhello.so。用gcc來(lái)創建動态庫。
在(zài)系統提示符下鍵入以(yǐ)下命令得到(dào)動态庫文件libmyhello.so,并用ls命令查看
gcc -shared -fPIC -o libmyhello.so hello.o (-o不(bù)可少)
1.6 在(zài)程序中使用動态庫
在(zài)程序中使用動态庫和(hé / huò)使用靜态庫完全一樣,也(yě)是(shì)在(zài)使用到(dào)這(zhè)些公用函數的(de)源程序中包含這(zhè)些公用函數的(de)原型聲明,然後在(zài)用gcc命令生成目标文件時(shí)指明動态庫名進行編譯。我們先運行gcc命令生成目标文件,再運行它看看結果。
輸入以(yǐ)下指令:gcc -o hello main.c -L. -lmyhello
這(zhè)裏出(chū)現了(le/liǎo)錯誤,我們看看錯誤提示,原來(lái)是(shì)找不(bù)到(dào)動态庫文件libmyhello.so。程序在(zài)運行時(shí),會在(zài)/usr/lib和(hé / huò)/lib等目錄中查找需要(yào / yāo)的(de)動态庫文件。若找到(dào),則載入動态庫,否則将提示類似上(shàng)述錯誤而(ér)終止程序運行。我們将文件libmyhello.so複制到(dào)目錄/usr/lib中,再試試。
2.Linux下靜态庫.a與.so庫文件的(de)生成與使用
如前面一樣,我們先生成舉例所需要(yào / yāo)的(de)文件
2.1 編輯所需程序
A1.c
A2.c
A.h
test.c
2.1 靜态庫.a文件的(de)生成與使用
(1) 生成目标文件(XXX.o)
指令如下:gcc -c A1.c A2.c
(2)生成靜态庫.a文件
指令如下:ar crv libafile.a A1.o A2.o
(3)使用.a 庫文件,創建可執行程序(若采用此種方式,需保證生成的(de).a 文件與.c 文件保存在(zài)同一目錄下,即都在(zài)當前目錄下)
指令如下:
cpp gcc -o test test.c libafile.a
./test
2.2 共享庫.so文件的(de)生成與使用
(1)生成目标文件(XXX.o)(此處生成.o 文件必須添加"-fpic"(小模式,代碼少),否則在(zài)生成.so文件時(shí)會出(chū)錯)
指令:gcc -c -fpic A1.c A2.c
(2)生成共享庫.so文件
指令:gcc -shared *.o -o libsofile.so
(3)使用.so 庫文件,創建可執行程序
指令:gcc -o test test.c libsofile.s
當輸入以(yǐ)下指令時(shí):./test
發現出(chū)現錯誤:
原因與之(zhī)前使用動态庫的(de)情況相同:找不(bù)到(dào)對用的(de).so文件,故需将對應 so 文件拷貝到(dào)對應路徑。
指令
如下:sudo cp libsofile.so /usr/lib
同時(shí)可直接使用 gcc -o test test.c -L. -lname,來(lái)使用相應庫文件
其中,
-L.:表示在(zài)當前目錄下,可自行定義路徑 path,即使用-Lpath 即可。
-lname:name:即對應庫文件的(de)名字(除開 lib),即若使用 libafile.a,則 name 爲(wéi / wèi) afile;若要(yào / yāo)使用 libsofile.so,則 name 爲(wéi / wèi) sofile。
3. 将第一次學習内容的(de)程序進行改編
目标:
(1)除了(le/liǎo)x2x函數之(zhī)外,再擴展寫一個(gè)x2y函數(功能自定),main函數代碼将調用x2x和(hé / huò)x2y ;将這(zhè)3個(gè)函數分别寫成單獨的(de)3個(gè) .c文件,并用gcc分别編譯爲(wéi / wèi)3個(gè).o 目标文件;将x2x、x2y目标文件用 ar工具生成1個(gè) .a 靜态庫文件, 然後用 gcc将 main函數的(de)目标文件與此靜态庫文件進行鏈接,生成最終的(de)可執行程序,記錄文件的(de)大(dà)小。
(2)将x2x、x2y目标文件用 ar工具生成1個(gè) .so 動态庫文件, 然後用 gcc将 main函數的(de)目标文件與此動态庫文件進行鏈接,生成最終的(de)可執行程序,記錄文件的(de)大(dà)小,并與之(zhī)前做對比。
3.1 完成目标(1)
(1)編輯所需程序
x2x.c
x2y.c
main.c
(2)将以(yǐ)上(shàng)文件用gcc編譯成.o文件
(3)将x2x.o和(hé / huò)x2y.o用 ar工具生成 .a 靜态庫文件
(4)使用gcc将main.c文件與此靜态庫文件進行鏈接
(5) 運行xymain程序
記錄文件大(dà)小爲(wéi / wèi)16888B
3.2 完成目标(2)
(1)将x2x.o、x2y.o文件用ar工具生成.so 動态庫文件
(2)用gcc将main.c文件與此動态庫文件進行鏈接
(3)運行somain
記錄文件大(dà)小爲(wéi / wèi)16888B
二、Gcc不(bù)是(shì)一個(gè)人(rén)在(zài)戰鬥。
GCC 的(de)意思也(yě)隻是(shì) GNU C Compiler 而(ér)已。經過了(le/liǎo)這(zhè)麽多年的(de)發展,GCC 已經不(bù)僅僅能支持 C語言;它現在(zài)還支持 Ada 語言、C++ 語言、Java 語言、Objective C 語言、Pascal 語言、COBOL語言,以(yǐ)及支持函數式編程和(hé / huò)邏輯編程的(de) Mercury 語言,等等。而(ér) GCC 也(yě)不(bù)再單隻是(shì) GNU C 語言編譯器的(de)意思了(le/liǎo),而(ér)是(shì)變成了(le/liǎo) GNU Compiler Collection 也(yě)即是(shì) GNU 編譯器家族的(de)意思了(le/liǎo)。另一方面,說(shuō)到(dào) GCC 對于(yú)操作系統平台及硬件平台支持,概括起來(lái)就(jiù)是(shì)一句話:無所不(bù)在(zài)。
1. Linux GCC常用命令
1.1 程序準備
1.2 程序的(de)簡單編譯
這(zhè)個(gè)程序的(de)一步到(dào)位的(de)編譯指令是(shì)gcc test.c -o main
1.3 程序的(de)分步編譯
實質上(shàng),上(shàng)述編譯過程是(shì)分爲(wéi / wèi)四個(gè)階段進行的(de),即預處理(也(yě)稱預編譯,Preprocessing)、編譯(Compilation)、彙編 (Assembly)和(hé / huò)連接(Linking)。
gcc -E test.c -o test.i //預處理
gcc -S test.i -o test.s //編譯
gcc -c test.s -o test.o //彙編
gcc test.o -o test //連接
執行以(yǐ)上(shàng)指令後生成以(yǐ)下文件,并查看運行結果