Linux中的動態庫和靜態庫(.a/.la/.so/.o)
本文由烏合之眾 lym瞎編,歡迎轉載 http://my.oschina.net/oloroso
本文由烏合之眾 lym瞎編,歡迎轉載 http://www.cnblogs.com/oloroso/
在windows下,一般可以通過文件的后綴名來識別文件的類型。在Linux下大致上也是可以的。但是要明確的一點是,在linux下,文件的后綴與文件的類型是沒有必然的聯系的。這只是約定俗稱的習慣罷了。
在linux 下進行C/C++開發,一般都是使用的gcc
編譯器,所以本文的講解以gcc
為主。
.o
文件,即目標文件。一般通過.c
或者.cpp
文件編譯而來,相當於VC
編譯出來的obj
文件.so
文件,shared object 共享庫(對象),相當於windows下的dll。.a
文件,archive 歸檔包,即靜態庫。其實質是多個.o
文件打包的結果,相當於VC
下的.lib
文件.la
文件,libtool archive 文件,是libtool自動生成的共享庫文件。
下面對這四種文件進行逐個說明。
C/C++程序編譯的過程
先說一下C/C++編譯的幾個過程。
- 預處理,展開頭文件,宏定義,條件編譯處理等。通過
gcc -E source.c -o source.i
或者cpp source.c
生成。 - 編譯。這里是一個狹義的編譯意義,指的是將預處理后的文件翻譯成匯編代碼的過程。通過
gcc -S source.i
生成。默認生成source.s
文件。 - 匯編。匯編即將上一步生成的匯編代碼翻譯成對應的二進制機器碼的過程。通過
gcc -c source.s
來生成source.o
文件。 - 鏈接。鏈接是將生成目標文件和其引用的各種符號等生成一個完整的可執行程序的過程。鏈接的時候會進行虛擬內存的重定向操作。
上面四個步驟就是C/C++程序編譯的幾個基本步驟。前面三個步驟都是很簡單,大多時候會合並為一個步驟。只有第四個步驟鏈接
是復雜一點的。很多時候我們編譯比較大的項目,報錯的往往是在鏈接的時候缺少某些庫,或者某些符號找不到定義,重定義等。
.o
文件(目標文件)
.o
文件就是C/C++源碼編譯的結果。即上面所說的C/C++編譯過程中的前三步。一般開發中很少將這三步分開來做,通常的做法是一步生成。
這里舉個例子,我們來寫一個函數int atoi(const char* str)
。
頭文件atoi.h
.#ifndef ATOI_H
.#define ATOI_H
int atoi(const char* str);
.#endif //! ATOI_H
源文件atoi.c
.#include <stdio.h>
.#include "atoi.h"
int atoi(const char* str)
{
int ret = 0;
if(str != NULL){
sscanf(str,"%d",&ret);
}
return ret;
}
創建atoi.o
直接使用命令gcc -c atoi.c -o atoi.o
或gcc -c atoi.c
來生成目標文件。
上面我們函數中調用了sscanf
這個C標准庫中的函數,那么它在.o
文件中會記錄這個存在,我們可以使用readelf
工具來查看一下。
o@Neo-kylin:~/lib_a_so$ ls
atoi.c atoi.h
o@Neo-kylin:~/lib_a_so$ gcc -c atoi.c
o@Neo-kylin:~/lib_a_so$ ll
總用量 20
drwxr-xr-x 2 o o 4096 10月 10 15:11 ./
drwxrwxr-x 5 1000 1000 4096 10月 10 14:32 ../
-rw-rw-r-- 1 o o 140 10月 10 15:07 atoi.c
-rw-rw-r-- 1 o o 75 10月 10 15:07 atoi.h
-rw-rw-r-- 1 o o 1536 10月 10 15:11 atoi.o
o@Neo-kylin:~/lib_a_so$ readelf -s atoi.o
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS atoi.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 60 FUNC GLOBAL DEFAULT 1 atoi
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND __isoc99_sscanf
這就是.o
文件了。它保存了編譯的時候引用到的符號(函數,全局變量等),這些符號,在鏈接的時候需要使用到。
使用atoi.o
使用atoi.o
的地方有很多,就不一一列舉了。這里先不說怎么用,后面生成.a
文件的時候用到了。
.a
文件(靜態庫文件)
靜態庫是多個.o
文件的打包的結果,前面已經說過了,其實不一定非要多個文件,一個.o
文件也可以打包為.a
文件。
這一步使用ar
工具來操作。ar
工具是用來創建, 修改和提取archives歸檔文件的工具,具體使用可以看manpages
。
ar [emulation options] [-]{dmpqrstx}[abcfilNoPsSuvV] [member-name] [count] archive-file file...
這個工具的作用看起來很簡單,但是其是很強大,且參數的設置很復雜的。這里不是為了介紹這個工具,不細說了。
創建atoi.a
我們先使用上面生成的atoi.o
文件來生成一個atoi.a
文件。
o@Neo-kylin:~/lib_a_so$ ls
atoi.c atoi.h atoi.o
o@Neo-kylin:~/lib_a_so$ ar -r atoi.a atoi.o
ar: creating atoi.a
o@Neo-kylin:~/lib_a_so$ ll
總用量 24
drwxr-xr-x 2 o o 4096 10月 10 15:35 ./
drwxrwxr-x 5 1000 1000 4096 10月 10 14:32 ../
-rw-rw-r-- 1 o o 1678 10月 10 15:35 atoi.a
-rw-rw-r-- 1 o o 140 10月 10 15:07 atoi.c
-rw-rw-r-- 1 o o 75 10月 10 15:07 atoi.h
-rw-rw-r-- 1 o o 1536 10月 10 15:11 atoi.o
-r
參數的意思是替換已存在的或插入新的文件到archive包中。
使用atoi.a
創建了atoi.a
文件后,我們就可以來使用它了。這里我們寫一個main
函數來調用atoi
。
main.c
文件
int main()
{
return atoi("5");
}
這一次我們先把main.c
編譯為main.o
文件。
o@Neo-kylin:~/lib_a_so$ ls
atoi.a atoi.c atoi.h atoi.o main.c
o@Neo-kylin:~/lib_a_so$ gcc -c main.c
o@Neo-kylin:~/lib_a_so$ ls
atoi.a atoi.c atoi.h atoi.o main.c main.o
然后使用ld
程序來鏈接main.o
和atoi.a
o@Neo-kylin:~/lib_a_so$ ld main.o atoi.a -o main
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
atoi.a(atoi.o): In function `atoi':
atoi.c:(.text+0x33): undefined reference to `__isoc99_sscanf'`
上面報了一個錯誤,原因是在atoi
函數中使用未定義的引用 __isoc99_sscanf
,這個問題我們可以通過鏈接上libc.a
或者libc.so
來解決這個問題。通常的情況下,都是鏈接libc.so
來解決的,如果使用glibc的靜態庫,那么你也必須將你的程序開源,不然這應該算是違反GPL協議的約定。
o@Neo-kylin:~/lib_a_so$ ld main.o atoi.a /lib64/libc.so.6 -o main
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400288
這里又報了一個警告,是沒有發現_start
符號的意思。這是因為沒有發現程序主入口點的原因。在C語言中,程序的入口函數是main
,但是在匯編中,程序的主入口函數是_start
。
這里我們可以把main.c
文件中的main
函數改為_start
函數,然后再編譯為main.o
再鏈接就沒有問題了。但是這不是正確的做法,這樣做雖然使用ld
來鏈接是不會報錯了,但是程序是運行不了的。會報錯誤
o@Neo-kylin:~/lib_a_so$ ld main.o atoi.a /lib64/libc.so.6 -o main
o@Neo-kylin:~/lib_a_so$ ./main
-bash: ./main: /lib/ld64.so.1: bad ELF interpreter: 沒有那個文件或目錄
正確的做法是鏈接上crt0.o、crti.o、crtn.o
等很多個文件就行了,不同的機器,需要鏈接的文件的位置可能不一樣。這個參數可能非常長,普通人記不住。
這個是可以怎么得到呢?我肯定不知道這些文件都在什么位置,但是gcc
編譯環境知道,我們可以使用gcc
來獲取。
o@Neo-kylin:~/lib_a_so$ gcc -v -o main main.o atoi.a
使用內建 specs。
目標:x86_64-redhat-linux
配置為:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
線程模型:posix
gcc 版本 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.4.7/:/usr/libexec/gcc/x86_64-redhat-linux/4.4.7/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/:/usr/lib/gcc/x86_64-redhat-linux/:/usr/libexec/gcc/x86_64-redhat-linux/4.4.7/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.4.7/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'main' '-mtune=generic'
/usr/libexec/gcc/x86_64-redhat-linux/4.4.7/collect2 --eh-frame-hdr --build-id -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../.. main.o atoi.a -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crtn.o
編譯之后我們可以來看以下程序運行結果是否正確。
o@Neo-kylin:~/lib_a_so$ ./main
o@Neo-kylin:~/lib_a_so$ echo $?
5
結果為5,與預期一致。
.so
文件(共享庫文件)
共享庫文件和windows下的dll
文件(dynamic link library)的概念是一樣的,都是在程序運行的時候進行動態鏈接,供程序調用的。
在linux 下可以使用ldd
命令來查看某個可執行文件需要鏈接哪些共享庫(動態庫),並可以確定這些要鏈接的共享庫在本機中的位置。
o@Neo-kylin:~/lib_a_so$ ldd main
linux-vdso.so.1 => (0x00007fffab1ff000)
libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
/lib64/ld-linux-x86-64.so.2 (0x000000305d000000)
這里要說以下動態庫的查找路徑。對於程序需要鏈接的動態庫xxx.so
,如果它在當前目錄下有,那么鏈接當前目錄下的。如果沒有,那么就鏈接系統/etc/ld.so.cache
(可通過ldconfig來更新)文件中查找xxx.so的路徑,如果都沒有,那么就會報錯啦。
我們在當前目錄創建一個libc.so.6
文件,然后再使用ldd
看一下。
o@Neo-kylin:~/lib_a_so$ touch libc.so.6 && chmod +x libc.so.6
o@Neo-kylin:~/lib_a_so$ ls -l libc.so.6
-rwxrwxr-x 1 o o 0 10月 10 17:15 libc.so.6
o@Neo-kylin:~/lib_a_so$ ldd main
./main: error while loading shared libraries: ./libc.so.6: file too short
可以看到,這時候是鏈接的當前目錄下的libc.so.6
這個文件,很可惜,出錯了。
其實在鏈接的時候,我們可以通過-Wl,-rpath=sopath
來指定運行時加載動態庫的路徑。這樣做的好處是可以把一些動態庫的位置信息不加入到/etc/ld.so.cache
中,已經避免和系統已有動態庫產生沖突的情況。(例如目標機器的glibc庫版本太低,而編譯程序的時候使用的高版本的,而出現”libc.so.6: version `GLIBC_2.14’ not found”類似的錯誤的情況)
注: -Wl: 表示后面的參數將傳給link程序ld,gcc編譯時候的鏈接實際上是調用ld進行的.
創建atoi.so
這里還是使用前面創建的atoi.c
文件創建atoi.so
文件。實際上我們這里創建atoi.so.1
文件,文件名后面的.1
代表的是版本號。動態庫因為使用的時候是動態鏈接的,而不是直接鏈接到目標程序文件中的。所以可能同時存在多個版本的情況,一般都會指定版本號。
通常使用libxxx.so.主版本號.副版本號
的形式來命名。
o@Neo-kylin:~/lib_a_so$ gcc -shared -o atoi.so.1 atoi.c
/usr/bin/ld: /tmp/ccLK0pLC.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/tmp/ccLK0pLC.o: could not read symbols: Bad value
collect2: ld 返回 1
o@Neo-kylin:~/lib_a_so$ gcc -fPIC -shared -o atoi.so.1 atoi.c
o@Neo-kylin:~/lib_a_so$ ls
atoi.a atoi.c atoi.h atoi.o atoi.so.1 main.c main.o
-share
該選項指定生成動態連接庫(讓連接器生成T類型的導出符號表,有時候也生成弱連接W類型的導出符號,后面介紹nm
工具的時候再說),不用該標志外部程序無法連接。相當於一個可執行文件。-fPIC
表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。
第一次沒有指定-fPIC
的時候出錯了,原因是針對可遷移R_X86_64_32平台,只讀數據段’.rodata’不能創建成共享對象,原因是在動態鏈接動態庫的時候,如果沒有編譯成位置無關代碼,那么鏈接的時候可能因為某些代碼的位置具有相關性,而在執行時出現錯誤。可執行文件在鏈接時就知道每一行代碼、每一個變量會被放到線性地址空間的什么位置,因此這些地址可以都作為常數寫到代碼里面。對於動態庫,只有加載的時候才知道。
如果代碼中沒有只讀數據段,那么就不會有這個問題。例如
o@Neo-kylin:~/lib_a_so$ cat >val.c
int a= 100;
o@Neo-kylin:~/lib_a_so$ gcc -shared -o val.so val.c
使用atoi.so
使用.so
文件的形式和使用.a
也差不多,也是使用ld
來進行鏈接。因為這過於復雜,還是使用gcc
來做這個操作(實際上gcc也是使用的ld)。
o@Neo-kylin:~/lib_a_so$ gcc -o main main.o atoi.so.1
o@Neo-kylin:~/lib_a_so$ ldd main
linux-vdso.so.1 => (0x00007fff56eaf000)
atoi.so.1 => not found
libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
/lib64/ld-linux-x86-64.so.2 (0x000000305d000000)
o@Neo-kylin:~/lib_a_so$ ./main
./main: error while loading shared libraries: atoi.so.1: cannot open shared object file: No such file or directory
上面執行的時候報錯,意思是找不到atoi.so.1
這個文件。原因是共享庫的查找目錄沒有當前目錄,我們可以添加環境變量LD_LIBRARY_PATH
來使系統動態載入器 (dynamic linker/loader)在當前目錄也查找。
o@Neo-kylin:~/lib_a_so$ export LD_LIBRARY_PATH=.
o@Neo-kylin:~/lib_a_so$ ldd main
linux-vdso.so.1 => (0x00007fff08fff000)
atoi.so.1 => ./atoi.so.1 (0x00007f9ed7ac6000)
libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
/lib64/ld-linux-x86-64.so.2 (0x000000305d000000)
o@Neo-kylin:~/lib_a_so$ ./main
o@Neo-kylin:~/lib_a_so$ echo $?
5
還有一種辦法,比添加環境變量更好使,也更具有可移植性,那就是編譯的時候指定運行的時候共享庫的加載路徑。gcc使用-Wl,-rpath=sopath
來指定,其中sopath
是共享庫放置的路徑(可以是絕對路徑,也可以是相對路徑)。
o@Neo-kylin:~/lib_a_so$ gcc -o main main.o -Wl,-rpath=. atoi.so.1
o@Neo-kylin:~/lib_a_so$ ldd main
linux-vdso.so.1 => (0x00007fff457ff000)
atoi.so.1 => ./atoi.so.1 (0x00007fb946d56000)
libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
/lib64/ld-linux-x86-64.so.2 (0x000000305d000000)
o@Neo-kylin:~/lib_a_so$ ./main
o@Neo-kylin:~/lib_a_so$ echo $?
5
動態庫還可以通過dlopen
/dlsym
等來使用,這里就不介紹了。
.la
文件(libtool archive)
以下的內容,參考使用 GNU Libtool 創建庫
這里先要說以下libtool
這個工具。
libtool是GNU的一個用來解決各個平台創建動態/靜態庫文件的不同操作的過於復雜的工具。它提供了使用抽象的接口進行動態/靜態庫的方法。
使用GNU Libtool
可以容易的在不同的系統中建立動態鏈接庫。它通過一個稱為Libtool
庫的抽象,隱藏了不同系統之間的差異,給開發人員提供了一致的的接口。對於大部分情況,開發人員甚至不用去查看相應的系統手冊,只需要掌握GNU Libtool
的用法就可以了。並且,使用 Libtool
的Makefile
也只需要編寫一次就可以在多個系統上使用。
libtool的使用
libtool的使用一般分為以下幾個步驟
1. 創建 Libtool 對象文件
2. 創建 Libtool 庫
3. 安裝 Libtool 庫
4. 使用 Libtool 庫
5. 卸載 Libtool 庫
1. 創建 Libtool 對象文件
o@Neo-kylin:~/lib_a_so$ ls
atoi.a atoi.c atoi.h main main.c
o@Neo-kylin:~/lib_a_so$ libtool --mode=compile gcc -c atoi.c
libtool: compile: gcc -c atoi.c -fPIC -DPIC -o .libs/atoi.o
libtool: compile: gcc -c atoi.c -o atoi.o >/dev/null 2>&1
o@Neo-kylin:~/lib_a_so$ ls -aR
.:
. .. atoi.a atoi.c atoi.h atoi.lo atoi.o .libs main main.c main.lo main.o
./.libs:
. .. atoi.o libatoi.a libatoi.la libatoi.lai libatoi.so libatoi.so.0 libatoi.so.0.0.0 main.o
創建libtool對象文件的過程,實際上是生成.o
、.so
、.a
文件的過程,同時還生成了一個.lo
文件。.lo
文件里面描述了兩個.o文件的路徑。這一步,就已經生成了相應的動態庫和靜態庫。
o@Neo-kylin:~/lib_a_so$ cat atoi.lo
# atoi.lo - a libtool object file
# Generated by ltmain.sh (GNU libtool) 2.2.6b
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# Name of the PIC object.
pic_object='.libs/atoi.o'
# Name of the non-PIC object
non_pic_object='atoi.o'
其中一個是用於生成靜態庫的,一個是用於生產動態庫的。
2. 創建 Libtool 庫
o@Neo-kylin:~/lib_a_so$ libtool --mode=link gcc -o libatoi.la atoi.lo -rpath /home/o/lib_a_so/lib -lc
libtool: link: rm -fr .libs/libatoi.a .libs/libatoi.la .libs/libatoi.lai .libs/libatoi.so .libs/libatoi.so.0 .libs/libatoi.so.0.0.0
libtool: link: gcc -shared .libs/atoi.o -lc -Wl,-soname -Wl,libatoi.so.0 -o .libs/libatoi.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libatoi.so.0" && ln -s "libatoi.so.0.0.0" "libatoi.so.0")
libtool: link: (cd ".libs" && rm -f "libatoi.so" && ln -s "libatoi.so.0.0.0" "libatoi.so")
libtool: link: ar cru .libs/libatoi.a atoi.o
libtool: link: ranlib .libs/libatoi.a
libtool: link: ( cd ".libs" && rm -f "libatoi.la" && ln -s "../libatoi.la" "libatoi.la" )
注意這里使用atoi.lo
作為輸入文件,並指定生成的目標文件為libatoi.la
。-rpath
選項是指定Libtool將這個庫安裝到的位置,如果省略了-rpath
選項,那么不生成動態鏈接庫。
因為在atoi
函數中使用了標准C庫函數sscanf
,所以帶上-lc
選項,Libtool 會記住這個依賴關系,后續在使用我們的庫時自動的將依賴的庫鏈接進來。
o@Neo-kylin:~/lib_a_so$ ls -aR
.:
. .. atoi.a atoi.c atoi.h atoi.lo atoi.o libatoi.la .libs main main.c main.lo main.o
./.libs:
. .. atoi.o libatoi.a libatoi.la libatoi.lai libatoi.so libatoi.so.0 libatoi.so.0.0.0 main.o
可以看到這次在當前目錄下生成了libatoi.la
文件,而.libs
目錄下的那個是一個符號鏈接,指向當前目錄下的這個文件。這其實是一個文本文件,里面的內容比較長,就不貼了。貼幾個比較重要的。
o@Neo-kylin:~/lib_a_so$ cat libatoi.la
dlname='libatoi.so.0'
library_names='libatoi.so.0.0.0 libatoi.so.0 libatoi.so'
old_library='libatoi.a'
dependency_libs=' -lc'
installed=no
shouldnotlink=no
dlopen=''
dlpreopen=''
libdir='/home/o/lib_a_so/lib'
3. 安裝 Libtool 庫
o@Neo-kylin:~/lib_a_so$ libtool --mode=install install -c libatoi.la /home/o/lib_a_so/lib
libtool: install: install -c .libs/libatoi.so.0.0.0 /home/o/lib_a_so/libatoi.so.0.0.0
libtool: install: (cd /home/o/lib_a_so && { ln -s -f libatoi.so.0.0.0 libatoi.so.0 || { rm -f libatoi.so.0 && ln -s libatoi.so.0.0.0 libatoi.so.0; }; })
libtool: install: (cd /home/o/lib_a_so && { ln -s -f libatoi.so.0.0.0 libatoi.so || { rm -f libatoi.so && ln -s libatoi.so.0.0.0 libatoi.so; }; })
libtool: install: install -c .libs/libatoi.lai /home/o/lib_a_so/libatoi.la
libtool: install: install -c .libs/libatoi.a /home/o/lib_a_so/libatoi.a
libtool: install: chmod 644 /home/o/lib_a_so/libatoi.a
libtool: install: ranlib /home/o/lib_a_so/libatoi.a
libtool: install: warning: remember to run `libtool --finish /home/o/lib_a_so/lib'
上面操作報了一個warning
,提示我們去運行 libtool --finish /home/o/lib_a_so/lib
,這個操作是使用libtool進行一個正確配置環境變量LD_LIBRARY_PATH
、LD_RUN_PATH
等的過程。如果不能正常的使用安裝好的庫,運行這個命令。
ls
查看一下,在當前目錄生成了 libatoi.a、libatoi.so、libatoi.so.0、libatoi.so.0.0.0這幾個文件。
o@Neo-kylin:~/lib_a_so$ ls
atoi.a atoi.c atoi.h atoi.lo atoi.o libatoi.a libatoi.la libatoi.so libatoi.so.0 libatoi.so.0.0.0 main main.c
4. 使用 Libtool 庫
先編譯main.c
文件
o@Neo-kylin:~/lib_a_so$ libtool --mode=compile gcc -c main.c
libtool: compile: gcc -c main.c -fPIC -DPIC -o .libs/main.o
libtool: compile: gcc -c main.c -o main.o >/dev/null 2>&1
然后使用Libtool
來進行鏈接操作。
o@Neo-kylin:~/lib_a_so$ libtool --mode=link gcc -o main main.lo /home/o/lib_a_so/libatoi.la
libtool: link: warning: library `/home/o/lib_a_so/libatoi.la' was moved.
libtool: link: warning: library `/home/o/lib_a_so/libatoi.la' was moved.
libtool: link: gcc -o main .libs/main.o /home/o/lib_a_so/libatoi.so -lc -Wl,-rpath -Wl,/home/o/lib_a_so -Wl,-rpath -Wl,/home/o/lib_a_so
從上面的輸出可以看到,實際上它還是調用的gcc
來進行的操作,但是它添加了選項-lc
,這就是Libtool
做的事情之一,它會解決依賴的問題。
o@Neo-kylin:~/lib_a_so$ ls
atoi.a atoi.h atoi.o libatoi.la libatoi.so.0 main main.lo
atoi.c atoi.lo libatoi.a libatoi.so libatoi.so.0.0.0 main.c main.o
o@Neo-kylin:~/lib_a_so$ ./main
o@Neo-kylin:~/lib_a_so$ echo $?
5
上面的操作默認使用的動態庫,可以使用-static-libtool-libs
選項來指定使用靜態庫。
o@Neo-kylin:~/lib_a_so$ ldd main
linux-vdso.so.1 => (0x00007fffd3bff000)
libatoi.so.0 => /home/o/lib_a_so/libatoi.so.0 (0x00007f984bdd0000)
libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
/lib64/ld-linux-x86-64.so.2 (0x000000305d000000)
使用-static-libtool-libs
選項重新生成。
o@Neo-kylin:~/lib_a_so$ libtool --mode=link gcc -o main main.lo /home/o/lib_a_so/libatoi.la -static-libtool-libs
libtool: link: warning: library `/home/o/lib_a_so/libatoi.la' was moved.
libtool: link: warning: library `/home/o/lib_a_so/libatoi.la' was moved.
libtool: link: gcc -o main .libs/main.o /home/o/lib_a_so/libatoi.a -lc
已經不不需要libatoi.so.0
了
o@Neo-kylin:~/lib_a_so$ ldd main
linux-vdso.so.1 => (0x00007fff1f3ac000)
libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
/lib64/ld-linux-x86-64.so.2 (0x000000305d000000)
5. 卸載 Libtool 庫
這是對安裝操作的反操作,會刪除安裝的所有庫文件。
o@Neo-kylin:~/lib_a_so$ libtool --mode=uninstall rm /home/o/lib_a_so/libatoi.la
libtool: uninstall: rm /home/o/lib_a_so/libatoi.la /home/o/lib_a_so/libatoi.so.0.0.0 /home/o/lib_a_so/libatoi.so.0 /home/o/lib_a_so/libatoi.so /home/o/lib_a_so/libatoi.a
http://www.cnblogs.com/oloroso/p/4874801.html