Linux C 庫的生成與使用

庫文件的存在極大的提升了 C/C++ 程序的複用性,本文簡單介紹如何生成、使用它們。html

命名約定: 咱們通常對庫文件約定以 lib 開頭,動態庫文件通常以 .so(*nix)或 .dll(Windows) 結尾,靜態庫文件通常以 .a(*nix)或 .lib(Windows) 結尾。linux

動態庫還分爲兩種用法:架構

  1. 在編譯期間聲明動態庫的存在,運行時由系統連接器自動連接
  2. 在運行期間動態加載和卸載,由用戶程序決定什麼時候連接

下面咱們以 Linux 爲例,Windows 的用法與此有所不一樣,但思路大同小異。函數

生成和使用靜態連接庫

將庫源碼編譯成目標文件:工具

$ gcc -c struct.c [-o struct.o]

打包庫文件:.net

$ ar cr libstruct.a struct.o # 注意參數順序不能錯

這裏的 artar 相似,是一種打包工具。插件

可使用 $ ar -t libstruct.a 命令 檢查包含哪些 .o 文件。設計

構建符號表:code

$ ranlib libstruct.a

這個命令不是必須的,是由於部分 ar 的實現集成了該功能,此時的 ranlib 就是一個空殼命令。htm

連接靜態庫,生成可執行文件:

$ gcc main.c -static -L . -lstruct -o main

由於咱們約定庫文件以 lib 開頭,因此這裏能夠省掉它。

設置庫文件的環境路徑:

  1. 定義 LD_LIBRARY_PATH 環境變量,添加動態庫文件的路徑。
  2. 把庫路徑添加到 ld.so.conf 文件中,而後用 ldconfig 加載。
  3. 使用 ldconfig <path-to-so-directory> 臨時加載。

編譯時,若是出現重名的動態庫和靜態庫,優先使用動態庫。

生成和使用動態連接庫

編譯動態連接庫文件:

$ gcc -shared -fPIC struct.c -o libstruct.so

這其實融合了好幾條命令,但咱們不作細述。

-fPIC 表示編譯爲「地址無關」的代碼,用於編譯共享庫。若是不加 -fPIC 則加載 so 文件的代碼段時,代碼段引用的數據對象須要重定位,重定位會修改代碼段的內容,這就形成每一個使用這個 so 文件代碼段的進程在內核裏都會生成這個 so 文件代碼段的副本。

通常老是用 -fPIC 來生成 .so 文件,也歷來不用fPIC來生成 .a 文件。

通常如下幾種狀況不須要:

  1. 該庫可能須要常常更新
  2. 該庫須要很是高的效率,尤爲是有不少全局量的使用時
  3. 該庫並不大
  4. 該庫基本不須要被多個應用程序共享

連接動態庫,生成可執行文件:

$ gcc main.c -L . lstruct -o main

通常咱們習慣將動態連接庫放入系統庫目錄:

$ gcc -shared -Wl,-soname,libstruct.so.1 -o libstruct.so.1 *.o

其中 -Wl 前綴的命令會傳遞給 ld 命令。咱們以前說過,gcc 融合了多條命令。

也就是在連接 .o 文件時會執行:

$ ld -soname libstruct.so.1

libstruct.so 用於在編譯期間使用 -lstruct 找到動態庫,而 libstruct.so.1 則在運行期間真正被連接。

$ mv libstruct.so.1 /opt/lib
$ ln -sf /opt/lib/libstruct.so.1 /opt/lib/libstruct.so.1
$ ln -sf /opt/lib/libstruct.so.1 /opt/lib/libstruct.so

查看程序對動態庫的依賴

$ ldd main

libstruct.so.1 => /opt/lib/libstruct.so.1 (0x00002aaaaaaac000)
libc.so.6 => /lib64/tls/libc.so.6 (0x0000003aa4e00000)
/lib64/ld-linux-x86-64.so.2 (0x0000003aa4c00000)

ldd 腳本中文詳解

查看 obj 文件的符號表

obj 文件的格式和組成多是系統差別性的一大致現,好比 Windows 下的 PE、Linux 和部分 Unix 下的 elf、Mac OS 下的 mach-o、aix 下的 xcoff。

使用 nmobjdumpreadelf 等命令能夠查看 .o .a .so 文件中的符號信息。

好比,下面這將打印出 hello.o 中未定義的符號:

$ nm -u hello.o

在運行時動態加載和卸載庫

須要應用程序但願設計成插件化的架構,這就須要能夠動態加載和卸載庫的機制。與動態連接不一樣的是,動態加載的意思是,編譯期間能夠對動態庫的存在一無所知,而是在運行期間經過用戶程序嘗試加載進來的。

經過 dlfcn.h 中的 dlopendlsymdlclose 等函數實現此種功能。

另外,使用 dlfcn機制須要在連接時使用 -rdynamic 選項,它將指示鏈接器把全部符號(而不只僅只是程序已使用到的外部符號,但不包括靜態符號,好比被 static 修飾的函數)都添加到動態符號表(即 .dynsym 表)裏。

GNU Libtool

現在許多軟件的編譯都採用 libtool 工具,libtool 是一個編譯連接包裝工具,實際只是一個腳本,用 libtool 編譯和連接會產生相似 .la 的文件,這種文件實際上是個文本文件,指向 .a 文件,並聲明一些版本信息。

更多

若是想更好地瞭解,須要多看 gcc / ar / ldd / nm 的參數。

相關文章
相關標籤/搜索