1. 程式人生 > >Linux中的動態庫和靜態庫(.a.la.so.o)

Linux中的動態庫和靜態庫(.a.la.so.o)

Linux中的動態庫和靜態庫(.a/.la/.so/.o)

原文地址:https://www.cnblogs.com/findumars/p/5421910.html

在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++編譯的幾個過程。

  1. 預處理,展開標頭檔案,巨集定義,條件編譯處理等。通過gcc -E source.c -o source.i或者cpp source.c生成。
  2. 編譯。這裡是一個狹義的編譯意義,指的是將預處理後的檔案翻譯成彙編程式碼的過程。通過gcc -S source.i生成。預設生成source.s
    檔案。
  3. 彙編。彙編即將上一步生成的彙編程式碼翻譯成對應的二進位制機器碼的過程。通過gcc -c source.s來生成source.o檔案。
  4. 連結。連結是將生成目標檔案和其引用的各種符號等生成一個完整的可執行程式的過程。連結的時候會進行虛擬記憶體的重定向操作。

上面四個步驟就是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.ogcc -c atoi.c來生成目標檔案。
上面我們函式中呼叫了sscanf這個C標準庫中的函式,那麼它在.o檔案中會記錄這個存在,我們可以使用readelf工具來檢視一下。

[email protected]-kylin:~/lib_a_so$ ls
atoi.c  atoi.h
[email protected]-kylin:~/lib_a_so$ gcc -c atoi.c
[email protected]-kylin:~/lib_a_so$ ll
總用量 20
drwxr-xr-x 2 o    o    4096 1010 15:11 ./
drwxrwxr-x 5 1000 1000 4096 1010 14:32 ../
-rw-rw-r-- 1 o    o     140 1010 15:07 atoi.c
-rw-rw-r-- 1 o    o      75 1010 15:07 atoi.h
-rw-rw-r-- 1 o    o    1536 1010 15:11 atoi.o
[email protected]-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檔案。

[email protected]-kylin:~/lib_a_so$ ls
atoi.c  atoi.h  atoi.o
[email protected]-kylin:~/lib_a_so$ ar -r atoi.a atoi.o
ar: creating atoi.a
[email protected]-kylin:~/lib_a_so$ ll
總用量 24
drwxr-xr-x 2 o    o    4096 1010 15:35 ./
drwxrwxr-x 5 1000 1000 4096 1010 14:32 ../
-rw-rw-r-- 1 o    o    1678 1010 15:35 atoi.a
-rw-rw-r-- 1 o    o     140 1010 15:07 atoi.c
-rw-rw-r-- 1 o    o      75 1010 15:07 atoi.h
-rw-rw-r-- 1 o    o    1536 1010 15:11 atoi.o

-r引數的意思是替換已存在的或插入新的檔案到archive包中。

說明:上面的引數也可以是ar -cr atoi.a atoi.o

-c的意思是隻編譯不連結

/******下面的是我參考博主學習的時候的 ************開始*
注意:
1.這裡一定要是從.o 檔案編譯成.a檔案,我自己在學習操作的時候直接從.c編譯的,雖然編譯成功了,也沒有報錯,但是在連結的時候報錯了
這裡寫圖片描述

在網上找了好久才把問題解決

  • [ ] 2.所要生成的.a檔案的名字前三位最好是lib,否則在連結的時候,就可能導致找不到這個庫

    *下面的是我參考博主學習的時候的********結束************/

使用atoi.a

建立了atoi.a檔案後,我們就可以來使用它了。這裡我們寫一個main函式來呼叫atoi

main.c檔案

int main()
{
    return atoi("5");
}

這一次我們先把main.c編譯為main.o檔案。

[email protected]:~/lib_a_so$ ls
atoi.a  atoi.c  atoi.h  atoi.o  main.c
[email protected]:~/lib_a_so$ gcc -c main.c 
[email protected]:~/lib_a_so$ ls
atoi.a  atoi.c  atoi.h  atoi.o  main.c  main.o

然後使用ld程式來連結main.oatoi.a

[email protected]:~/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協議的約定。

[email protected]:~/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來連結是不會報錯了,但是程式是執行不了的。會報錯誤

[email protected]:~/lib_a_so$ ld main.o atoi.a /lib64/libc.so.6 -o main
[email protected]:~/lib_a_so$ ./main 
-bash: ./main: /lib/ld64.so.1: bad ELF interpreter: 沒有那個檔案或目錄

正確的做法是連結上crt0.o、crti.o、crtn.o等很多個檔案就行了,不同的機器,需要連結的檔案的位置可能不一樣。這個引數可能非常長,普通人記不住。
這個是可以怎麼得到呢?我肯定不知道這些檔案都在什麼位置,但是gcc編譯環境知道,我們可以使用gcc來獲取。

[email protected]-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

編譯之後我們可以來看以下程式執行結果是否正確。

[email protected]Neo-kylin:~/lib_a_so$ ./main 
[email protected]Neo-kylin:~/lib_a_so$ echo $?
5

結果為5,與預期一致。

/*下面的是我參考博主學習的時候的 *開始************

補充說明:

1.一般的靜態庫可以放在和工程裡面,也可以放在linux為使用者準備的目錄下/user/local/lib下。這樣就不需要連結絕對路徑

2.如果.a檔案不再指定位置(/user/lib /uer/local/lib ./)加絕對路徑的時候就不需要 -L. 和-l引數了,需要指定路徑和檔名的全稱
這裡寫圖片描述

3.引數含義:

-L.:表示要連線的庫在當前目錄中
-lmd5.a:編譯器查詢動態連線庫時有隱含的命名規則,即在給出的名字前面加上lib,後
面加上.so來確定庫的名稱

4.所以通過上面的解釋,那麼編譯的時候直接使用當前檔案下的.a檔案(前提是.a檔案在)應該也是可以的
這裡寫圖片描述

果然可以,執行程式,結果正確

2.所要生成的.a檔案的名字前三位最好是lib,否則在連結的時候,就可能導致找不到這個庫

******下面的是我參考博主學習的時候的****結束*******************/

### 使用atoi.a


.so檔案(共享庫檔案)

共享庫檔案和windows下的dll檔案(dynamic link library)的概念是一樣的,都是在程式執行的時候進行動態連結,供程式呼叫的。
在linux 下可以使用ldd命令來檢視某個可執行檔案需要連結哪些共享庫(動態庫),並可以確定這些要連結的共享庫在本機中的位置。

[email protected]:~/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的路徑,如果都沒有,那麼就會報錯啦。

這裡應該也可以連結使用者目錄下的/user/local/lib64檔案下的.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 1010 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.主版本號.副版本號的形式來命名。

[email protected]:~/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
[email protected]:~/lib_a_so$ gcc -fPIC -shared -o atoi.so.1 atoi.c
[email protected]:~/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)。

[email protected]:~/lib_a_so$ gcc -o main main.o atoi.so.1 
[email protected]:~/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)
[email protected]:~/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是共享庫放置的路徑(可以是絕對路徑,也可以是相對路徑)。

[email protected]-kylin:~/lib_a_so$ gcc -o main main.o -Wl,-rpath=. atoi.so.1 
[email protected]-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)
[email protected]-kylin:~/lib_a_so$ ./main 
[email protected]-kylin:~/lib_a_so$ echo $?
5

動態庫還可以通過dlopen/dlsym等來使用,這裡就不介紹了。

/**************下面的是我參考博主學習的時候的 ********************開始**********************************************************************************

1.前面的不加-wl,-rpath引數時和博主的現象一樣,加了引數後正常,得到正確的結果了

這裡寫圖片描述

思考:

a.如果該.so檔案不在當前路徑下是否可以指定路徑後正確編譯?

這裡寫圖片描述

可以看到這樣是可以的,注意-Wl中的w是大寫

如果動態庫在當前路徑下,是否可以直接用gcc編譯,而不需要指定路徑
這裡寫圖片描述

也是可以的

b.是否可以在編譯的時候就加上-wl,-rpath引數,然後後面連結的時候就不需要再加上引數了?
這裡寫圖片描述

看來是不行

c.是否可以將.so檔案放在/user/local/lib目錄下,效果和放在當前目錄下的是一樣的?

我的環境是centos7,並不可以

***下面的是我參考博主學習的時候的**********************結束*************************************************************************************/

下面的內容也分享下


.la檔案(libtool archive)

以下的內容,參考使用 GNU Libtool 建立庫
這裡先要說以下libtool這個工具。
libtool是GNU的一個用來解決各個平臺建立動態/靜態庫檔案的不同操作的過於複雜的工具。它提供了使用抽象的介面進行動態/靜態庫的方法。
使用GNU Libtool可以容易的在不同的系統中建立動態連結庫。它通過一個稱為Libtool庫的抽象,隱藏了不同系統之間的差異,給開發人員提供了一致的的介面。對於大部分情況,開發人員甚至不用去檢視相應的系統手冊,只需要掌握GNU Libtool的用法就可以了。並且,使用 LibtoolMakefile也只需要編寫一次就可以在多個系統上使用。

libtool的使用

libtool的使用一般分為以下幾個步驟
\1. 建立 Libtool 物件檔案
\2. 建立 Libtool 庫
\3. 安裝 Libtool 庫
\4. 使用 Libtool 庫
\5. 解除安裝 Libtool 庫

1. 建立 Libtool 物件檔案

[email protected]:~/lib_a_so$ ls
atoi.a  atoi.c  atoi.h  main  main.c
[email protected]:~/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
[email protected]:~/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檔案的路徑。這一步,就已經生成了相應的動態庫和靜態庫。

[email protected]:~/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 庫

[email protected]:~/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 會記住這個依賴關係,後續在使用我們的庫時自動的將依賴的庫連結進來。

[email protected]:~/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 庫

[email protected]:~/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_PATHLD_RUN_PATH等的過程。如果不能正常的使用安裝好的庫,執行這個命令。

ls檢視一下,在當前目錄生成了 libatoi.a、libatoi.so、libatoi.so.0、libatoi.so.0.0.0這幾個檔案。

[email protected]:~/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檔案

[email protected]-kylin:~/lib_a_so$ libtool --mode=compile gcc -c main.c
libtool: compile:  gcc -c<