G++(Linux)
-I(大寫i),到指定目錄搜索頭文件
-L,到指定目錄搜索庫文件
-l(小寫L),到前面指定的目錄下添加庫文件進行鏈接
-o,為生成文件重命名
???g++ -E xxx.cpp
???不加-o 生成xxx.i
???加-o xxx123 生成xxx123
-E,將源代碼 .c/.cpp文件進行預(yù)編譯
???將所有#define刪除
???并且展開所有的宏定義處理所有的條件預(yù)編譯指令,如#if #ifdef #undef #ifndef #endif #elif處理#include
???將包含的文件插入到此處,這是一個遞歸的過程
???刪除所有注釋 // /* */添加行號和文件名標(biāo)識,以便于編譯時產(chǎn)生的錯誤警告能顯示行號
???保留#pragma編譯器指令
???g++ -E xxx.cpp 生成xxx.i
-S,將預(yù)處理完的.i文件進行一系列的詞法分析、語法分析、語義分析及優(yōu)化后生成響應(yīng)的匯編代碼文件
???g++ -S xxx.cpp/xxx.i 生成xxx.s
-c,生成目標(biāo)文件 以xxx.o結(jié)尾,可以用于生成庫文件
???g++ -c xxx.cpp/xxx.s 生成xxx.o
靜態(tài)庫
在g++ -c命令創(chuàng)建完.o結(jié)尾的目標(biāo)文件后,可以通過ar命令打包成靜態(tài)庫
實際上ar命令是用于創(chuàng)建備存文件的,將一些文件打包成一個文件,類似于壓縮文件,但不完全等于壓縮文件
實際上在調(diào)用g++ -l(小寫L)指令的時候?qū)浯嫖募械?o文件提取出來進行鏈接操作
但是在鏈接中需要用到-L來指示庫文件所在目錄,用-l(小寫L)來指定庫文件
而-l(小寫L)時會補充xxx前后的lib與.a,所以在使用ar命令來打包靜態(tài)庫的時候需要直接命名為libxxx.a
在實際上以下兩種鏈接方法完全等效
$ g++ main.cpp -L./lib -lmath
$ g++ main.cpp math.o
一般對.o結(jié)尾的目標(biāo)文件生成靜態(tài)庫的時候,使用以下參數(shù)
ar rcs [備存文件] [成員文件]
(靜態(tài)庫文件名) (目標(biāo)文件名)
例 ar rcs libmath.a math.o
更多ar命令,請看以下
Linux ar命令
動態(tài)庫
動態(tài)庫編譯方式如同靜態(tài)庫一樣,先要編譯出目標(biāo)文件.o,然后組成.so庫—— g++ -c math.cpp -o math.o
但是這次編譯需要用到-fPIC指令來編譯,在g++ -fPIC -c math.cpp -o math.o
獲得了目標(biāo)文件.o后,需要用指令-shared聲明將.o文件鏈接成.so庫,g++ -shared -o libmath.so math.o
實際上生成.so庫可以合成一個指令,g++ -fPIC -shared math.cpp -o libmath.so
編譯main.cpp時,需要和鏈接靜態(tài)庫一樣,要用到-L與-l(小寫L),但是要添加-ldl,g++ math.cpp -L ./so/ -lmath -ldl
關(guān)于載入動態(tài)庫的規(guī)則:
在Linux中,系統(tǒng)會按照一定方式搜索動態(tài)庫文件
- (僅限ELF)如果調(diào)用程序的可執(zhí)行文件包含 DT_RPATH 標(biāo)記,并且不包含 DT_RUNPATH 標(biāo)記,則會搜索 DT_RPATH 標(biāo)記中列出的目錄。
- 如果在程序啟動時,環(huán)境變量 LD_LIBRARY_PATH 被定義為包含以冒號分隔的目錄列表,則會搜索這些目錄。 (作為安全措施,set-user-ID 和 set-group-ID程序?qū)⒑雎源俗兞俊#?/li>
- (僅限ELF)如果調(diào)用程序的可執(zhí)行文件包含?DT_RUNPATH?標(biāo)記,則搜索該標(biāo)記中列出的目錄。
- 檢查緩存文件/etc/ld.so.cache(由ldconfig(8)維護)以查看它是否包含filename的條目。
- 搜索目錄 /lib和 /usr/lib(按此順序)。
例如創(chuàng)建環(huán)境變量 LD_LIBRARY_PATH ,在中間加入動態(tài)庫的目錄,export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./so
在執(zhí)行調(diào)用動態(tài)庫時,需要對以上規(guī)則內(nèi)的目錄進行添加對應(yīng)動態(tài)庫,也可以用ldd命令查看可執(zhí)行文件中調(diào)用的動態(tài)庫是否存在對應(yīng)的文件,ldd a.out
如何執(zhí)行動態(tài)庫中的程序?
一、隱式執(zhí)行
二、顯示執(zhí)行
一、隱式執(zhí)行
庫文件中的可執(zhí)行函數(shù)都聲明為靜態(tài)(static),int main()中直接調(diào)用該靜態(tài)函數(shù),道理懂得都懂。
二、顯示執(zhí)行
使用<dlfcn.h>函數(shù)庫中各種庫函數(shù),加載動態(tài)庫,執(zhí)行動態(tài)庫中程序etc.
介紹幾個常用動態(tài)庫函數(shù)
1.dlopen
??void* dlopen(const char* filename,int mode)
??filename 為動態(tài)庫名,mode為載入動態(tài)庫的方式模式。
??mode:
??1、解析方式
??RTLD_LAZY:在dlopen返回前,對于動態(tài)庫中的未定義的符號不執(zhí)行解析(只對函數(shù)引用有效,對于變量引用總是立即解析)。
??RTLD_NOW: 需要在dlopen返回前,解析出所有未定義符號,如果解析不出來,在dlopen會返回NULL,錯誤為:: undefined symbol: xxxx.......
??2、作用范圍,可與解析方式通過“|”組合使用。
??RTLD_GLOBAL:動態(tài)庫中定義的符號可被其后打開的其它庫解析。
??RTLD_LOCAL: 與RTLD_GLOBAL作用相反,動態(tài)庫中定義的符號不能被其后打開的其它庫重定位。如果沒有指明是RTLD_GLOBAL還是RTLD_LOCAL,則缺省為RTLD_LOCAL。
??3、作用方式
??RTLD_NODELETE: 在dlclose()期間不卸載庫,并且在以后使用dlopen()重新加載庫時不初始化庫中的靜態(tài)變量。這個flag不是POSIX-2001標(biāo)準(zhǔn)。
??RTLD_NOLOAD: 不加載庫。可用于測試庫是否已加載(dlopen()返回NULL說明未加載,否則說明已加載),也可用于改變已加載庫的flag,如:先前加載庫的flag為RTLD_LOCAL,用dlopen(RTLD_NOLOAD|RTLD_GLOBAL)后flag將變成RTLD_GLOBAL。這個flag不是POSIX-2001標(biāo)準(zhǔn)。
??RTLD_DEEPBIND:在搜索全局符號前先搜索庫內(nèi)的符號,避免同名符號的沖突。這個flag不是POSIX-2001標(biāo)準(zhǔn)。
??dlopen執(zhí)行后返回一個handle
2.dlsym
??void* dlsym(void* handle,const char* symbol)
??執(zhí)行完后返回handle中的對應(yīng)symbol符號的地址
??其中handle就是dlopen執(zhí)行完后的handle,symbol就是要求獲取的函數(shù)或全局變量的名稱
??關(guān)于symbol,其中有說法的是符號名
??因為在c++中為了保證多態(tài)性,函數(shù)都能進行重載,所以在c++中的符號名不是完全按照函數(shù)名來命名,會按照不同編譯器的方式進行改編
??而c中不需要保證多態(tài)性,所以符號名都為函數(shù)名。
??所以在需要進行c方式編譯的代碼段,用 extern "C" 來聲明這一段代碼用C語言來編譯,使用dlsym時,便可以通過函數(shù)名來調(diào)用庫中的函數(shù)
3.dlclose
??int dlclose (void *handle)
??傳入handle,卸載handle所指的動態(tài)庫內(nèi)存區(qū)域
4.dlerror
??char *dlerror(void)
??返回錯誤信息