溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Linux中的動態(tài)庫和靜態(tài)庫用法介紹

發(fā)布時間:2021-08-11 13:34:12 來源:億速云 閱讀:202 作者:chen 欄目:云計算

本篇內(nèi)容介紹了“Linux中的動態(tài)庫和靜態(tài)庫用法介紹”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

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

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

    • libtool的使用

    • 1. 創(chuàng)建 Libtool 對象文件

    • 2. 創(chuàng)建 Libtool 庫

    • 3. 安裝 Libtool 庫

    • 4. 使用 Libtool 庫

    • 5. 卸載 Libtool 庫

    • 創(chuàng)建atoi.so

    • 使用atoi.so

    • 創(chuàng)建atoi.a

    • 使用atoi.a

    • 創(chuàng)建atoi.o

    • 使用atoi.o

    • C/C++程序編譯的過程

    • .o文件(目標(biāo)文件)

    • .a文件(靜態(tài)庫文件)

    • .so文件(共享庫文件)

    • .la文件(libtool archive)

    在windows下,一般可以通過文件的后綴名來識別文件的類型。在Linux下大致上也是可以的。但是要明確的一點是,在linux下,文件的后綴與文件的類型是沒有必然的聯(lián)系的。這只是約定俗稱的習(xí)慣罷了。

    在linux 下進(jìn)行C/C++開發(fā),一般都是使用的gcc編譯器,所以本文的講解以gcc為主。

    • .o文件,即目標(biāo)文件。一般通過.c或者.cpp文件編譯而來,相當(dāng)于VC編譯出來的obj文件

    • .so文件,shared object 共享庫(對象),相當(dāng)于windows下的dll。

    • .a文件,archive 歸檔包,即靜態(tài)庫。其實質(zhì)是多個.o文件打包的結(jié)果,相當(dāng)于VC下的.lib文件

    • .la文件,libtool archive 文件,是libtool自動生成的共享庫文件。

    下面對這四種文件進(jìn)行逐個說明。

    C/C++程序編譯的過程

    先說一下C/C++編譯的幾個過程。

    1. 預(yù)處理,展開頭文件,宏定義,條件編譯處理等。通過gcc -E source.c -o source.i或者cpp source.c生成。

    2. 編譯。這里是一個狹義的編譯意義,指的是將預(yù)處理后的文件翻譯成匯編代碼的過程。通過gcc -S source.i生成。默認(rèn)生成source.s文件。

    3. 匯編。匯編即將上一步生成的匯編代碼翻譯成對應(yīng)的二進(jìn)制機器碼的過程。通過gcc -c source.s來生成source.o文件。

    4. 鏈接。鏈接是將生成目標(biāo)文件和其引用的各種符號等生成一個完整的可執(zhí)行程序的過程。鏈接的時候會進(jìn)行虛擬內(nèi)存的重定向操作。

    上面四個步驟就是C/C++程序編譯的幾個基本步驟。前面三個步驟都是很簡單,大多時候會合并為一個步驟。只有第四個步驟鏈接是復(fù)雜一點的。很多時候我們編譯比較大的項目,報錯的往往是在鏈接的時候缺少某些庫,或者某些符號找不到定義,重定義等。

    .o文件(目標(biāo)文件)

    .o文件就是C/C++源碼編譯的結(jié)果。即上面所說的C/C++編譯過程中的前三步。一般開發(fā)中很少將這三步分開來做,通常的做法是一步生成。

    這里舉個例子,我們來寫一個函數(shù)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;
    }

    創(chuàng)建atoi.o

    直接使用命令gcc -c atoi.c -o atoi.ogcc -c atoi.c來生成目標(biāo)文件。
    上面我們函數(shù)中調(diào)用了sscanf這個C標(biāo)準(zhǔn)庫中的函數(shù),那么它在.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文件了。它保存了編譯的時候引用到的符號(函數(shù),全局變量等),這些符號,在鏈接的時候需要使用到。

    使用atoi.o

    使用atoi.o的地方有很多,就不一一列舉了。這里先不說怎么用,后面生成.a文件的時候用到了。

    .a文件(靜態(tài)庫文件)

    靜態(tài)庫是多個.o文件的打包的結(jié)果,前面已經(jīng)說過了,其實不一定非要多個文件,一個.o文件也可以打包為.a文件。
    這一步使用ar工具來操作。ar工具是用來創(chuàng)建, 修改和提取archives歸檔文件的工具,具體使用可以看manpages

    ar [emulation options] [-]{dmpqrstx}[abcfilNoPsSuvV] [member-name] [count] archive-file file...

    這個工具的作用看起來很簡單,但是其是很強大,且參數(shù)的設(shè)置很復(fù)雜的。這里不是為了介紹這個工具,不細(xì)說了。

    創(chuàng)建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參數(shù)的意思是替換已存在的或插入新的文件到archive包中。

    使用atoi.a

    創(chuàng)建了atoi.a文件后,我們就可以來使用它了。這里我們寫一個main函數(shù)來調(diào)用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.oatoi.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函數(shù)中使用未定義的引用 __isoc99_sscanf,這個問題我們可以通過鏈接上libc.a或者libc.so來解決這個問題。通常的情況下,都是鏈接libc.so來解決的,如果使用glibc的靜態(tài)庫,那么你也必須將你的程序開源,不然這應(yīng)該算是違反GPL協(xié)議的約定。

    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

    這里又報了一個警告,是沒有發(fā)現(xiàn)_start符號的意思。這是因為沒有發(fā)現(xiàn)程序主入口點的原因。在C語言中,程序的入口函數(shù)是main,但是在匯編中,程序的主入口函數(shù)是_start。

    這里我們可以把main.c文件中的main函數(shù)改為_start函數(shù),然后再編譯為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等很多個文件就行了,不同的機器,需要鏈接的文件的位置可能不一樣。這個參數(shù)可能非常長,普通人記不住。
    這個是可以怎么得到呢?我肯定不知道這些文件都在什么位置,但是gcc編譯環(huán)境知道,我們可以使用gcc來獲取。

    o@Neo-kylin:~/lib_a_so$ gcc -v -o main main.o atoi.a 
    使用內(nèi)建 specs。
    目標(biāo):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

    編譯之后我們可以來看以下程序運行結(jié)果是否正確。

    o@Neo-kylin:~/lib_a_so$ ./main 
    o@Neo-kylin:~/lib_a_so$ echo $?
    5

    結(jié)果為5,與預(yù)期一致。

    .so文件(共享庫文件)

    共享庫文件和windows下的dll文件(dynamic link library)的概念是一樣的,都是在程序運行的時候進(jìn)行動態(tài)鏈接,供程序調(diào)用的。
    在linux 下可以使用ldd命令來查看某個可執(zhí)行文件需要鏈接哪些共享庫(動態(tài)庫),并可以確定這些要鏈接的共享庫在本機中的位置。

    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)

    這里要說以下動態(tài)庫的查找路徑。對于程序需要鏈接的動態(tài)庫xxx.so,如果它在當(dāng)前目錄下有,那么鏈接當(dāng)前目錄下的。如果沒有,那么就鏈接系統(tǒng)/etc/ld.so.cache(可通過ldconfig來更新)文件中查找xxx.so的路徑,如果都沒有,那么就會報錯啦。
    我們在當(dāng)前目錄創(chuàng)建一個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

    可以看到,這時候是鏈接的當(dāng)前目錄下的libc.so.6這個文件,很可惜,出錯了。
    其實在鏈接的時候,我們可以通過-Wl,-rpath=sopath來指定運行時加載動態(tài)庫的路徑。這樣做的好處是可以把一些動態(tài)庫的位置信息不加入到/etc/ld.so.cache中,已經(jīng)避免和系統(tǒng)已有動態(tài)庫產(chǎn)生沖突的情況。(例如目標(biāo)機器的glibc庫版本太低,而編譯程序的時候使用的高版本的,而出現(xiàn)”libc.so.6: version `GLIBC_2.14’ not found”類似的錯誤的情況)

    注: -Wl: 表示后面的參數(shù)將傳給link程序ld,gcc編譯時候的鏈接實際上是調(diào)用ld進(jìn)行的.

    創(chuàng)建atoi.so

    這里還是使用前面創(chuàng)建的atoi.c文件創(chuàng)建atoi.so文件。實際上我們這里創(chuàng)建atoi.so.1文件,文件名后面的.1代表的是版本號。動態(tài)庫因為使用的時候是動態(tài)鏈接的,而不是直接鏈接到目標(biāo)程序文件中的。所以可能同時存在多個版本的情況,一般都會指定版本號。
    通常使用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ài)連接庫(讓連接器生成T類型的導(dǎo)出符號表,有時候也生成弱連接W類型的導(dǎo)出符號,后面介紹nm工具的時候再說),不用該標(biāo)志外部程序無法連接。相當(dāng)于一個可執(zhí)行文件。
    -fPIC表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關(guān)的所以動態(tài)載入時是通過代碼拷貝的方式來滿足不同進(jìn)程的需要,而不能達(dá)到真正代碼段共享的目的。

    第一次沒有指定-fPIC的時候出錯了,原因是針對可遷移R_X86_64_32平臺,只讀數(shù)據(jù)段’.rodata’不能創(chuàng)建成共享對象,原因是在動態(tài)鏈接動態(tài)庫的時候,如果沒有編譯成位置無關(guān)代碼,那么鏈接的時候可能因為某些代碼的位置具有相關(guān)性,而在執(zhí)行時出現(xiàn)錯誤。可執(zhí)行文件在鏈接時就知道每一行代碼、每一個變量會被放到線性地址空間的什么位置,因此這些地址可以都作為常數(shù)寫到代碼里面。對于動態(tài)庫,只有加載的時候才知道。

    如果代碼中沒有只讀數(shù)據(jù)段,那么就不會有這個問題。例如

    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來進(jìn)行鏈接。因為這過于復(fù)雜,還是使用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

    上面執(zhí)行的時候報錯,意思是找不到atoi.so.1這個文件。原因是共享庫的查找目錄沒有當(dāng)前目錄,我們可以添加環(huán)境變量LD_LIBRARY_PATH來使系統(tǒng)動態(tài)載入器 (dynamic linker/loader)在當(dāng)前目錄也查找。

    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

    還有一種辦法,比添加環(huán)境變量更好使,也更具有可移植性,那就是編譯的時候指定運行的時候共享庫的加載路徑。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

    動態(tài)庫還可以通過dlopen/dlsym等來使用,這里就不介紹了。

    .la文件(libtool archive)

    以下的內(nèi)容,參考使用 GNU Libtool 創(chuàng)建庫
    這里先要說以下libtool這個工具。
    libtool是GNU的一個用來解決各個平臺創(chuàng)建動態(tài)/靜態(tài)庫文件的不同操作的過于復(fù)雜的工具。它提供了使用抽象的接口進(jìn)行動態(tài)/靜態(tài)庫的方法。
    使用GNU Libtool可以容易的在不同的系統(tǒng)中建立動態(tài)鏈接庫。它通過一個稱為Libtool庫的抽象,隱藏了不同系統(tǒng)之間的差異,給開發(fā)人員提供了一致的的接口。對于大部分情況,開發(fā)人員甚至不用去查看相應(yīng)的系統(tǒng)手冊,只需要掌握GNU Libtool的用法就可以了。并且,使用 LibtoolMakefile也只需要編寫一次就可以在多個系統(tǒng)上使用。

    libtool的使用

    libtool的使用一般分為以下幾個步驟
    1. 創(chuàng)建 Libtool 對象文件
    2. 創(chuàng)建 Libtool 庫
    3. 安裝 Libtool 庫 
    4. 使用 Libtool 庫
    5. 卸載 Libtool 庫

    1. 創(chuàng)建 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

    創(chuàng)建libtool對象文件的過程,實際上是生成.o.so、.a文件的過程,同時還生成了一個.lo文件。.lo文件里面描述了兩個.o文件的路徑。這一步,就已經(jīng)生成了相應(yīng)的動態(tài)庫和靜態(tài)庫。

    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'

    其中一個是用于生成靜態(tài)庫的,一個是用于生產(chǎn)動態(tài)庫的。

    2. 創(chuàng)建 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作為輸入文件,并指定生成的目標(biāo)文件為libatoi.la。
    -rpath選項是指定Libtool將這個庫安裝到的位置,如果省略了-rpath選項,那么不生成動態(tài)鏈接庫。
    因為在atoi函數(shù)中使用了標(biāo)準(zhǔn)C庫函數(shù)sscanf,所以帶上-lc選項,Libtool 會記住這個依賴關(guān)系,后續(xù)在使用我們的庫時自動的將依賴的庫鏈接進(jìn)來。

    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

    可以看到這次在當(dāng)前目錄下生成了libatoi.la文件,而.libs目錄下的那個是一個符號鏈接,指向當(dāng)前目錄下的這個文件。這其實是一個文本文件,里面的內(nèi)容比較長,就不貼了。貼幾個比較重要的。

    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,提示我們?nèi)ミ\行 libtool --finish /home/o/lib_a_so/lib,這個操作是使用libtool進(jìn)行一個正確配置環(huán)境變量LD_LIBRARY_PATH、LD_RUN_PATH等的過程。如果不能正常的使用安裝好的庫,運行這個命令。

    ls查看一下,在當(dāng)前目錄生成了 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來進(jìn)行鏈接操作。

    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

    從上面的輸出可以看到,實際上它還是調(diào)用的gcc來進(jìn)行的操作,但是它添加了選項-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

    上面的操作默認(rèn)使用的動態(tài)庫,可以使用-static-libtool-libs選項來指定使用靜態(tài)庫。

    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

    已經(jīng)不不需要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

    “Linux中的動態(tài)庫和靜態(tài)庫用法介紹”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI