linux下 GCC編譯鏈結靜態庫 動態庫

2021-10-05 11:23:06 字數 4612 閱讀 7647

目錄

回到頂部

有時候需要把一組**編譯成乙個庫,這個庫在很多專案中都要用到,例如libc就是這樣乙個庫, 我們在不同的程式中都會用到libc中的庫函式(例如printf),也會用到libc中的變數(例如以後 要講到的environ變數)。本文將介紹怎麼建立這樣乙個庫。

這些檔案的目錄結構是:

$ tree 

. |-- main.c

`-- stack

|-- is_empty.c

|-- pop.c

|-- push.c

|-- stack.c

`-- stack.h

1 directory, 6 files

我們把stack.c、push.c、pop.c、is_empty.c編譯成目標檔案:

$ gcc -c stack/stack.c stack/push.c stack/pop.c stack/is_empty.c
然後打包成乙個靜態庫libstack.a:

$ ar rs libstack.a stack.o push.o pop.o is_empty.o 

ar: creating libstack.a

庫檔名都是以lib開頭的,靜態庫以.a作為字尾,表示archive。ar命令類似於tar命令,起乙個打包的作用,但是把目標檔案打包成靜態庫只能用ar命令而不能用tar命令。選項r表示將後面的檔案列表新增到檔案包,如果檔案包不存在就建立它,如果檔案包中已有同名檔案就替換成新的。s是專用於生成靜態庫的,表示為靜態庫建立索引,這個索引被鏈結器使用。ranlib命令也可以為靜態庫建立索引,以上命令等價於:

$ ar r libstack.a stack.o push.o pop.o is_empty.o 

$ ranlib libstack.a

$ gcc main.c -l. -lstack -istack -o main
-l選項告訴編譯器去**找需要的庫檔案,-l.表示在當前目錄找。-lstack告訴編譯器要鏈 接libstack庫,-i選項告訴編譯器去**找標頭檔案。注意,即使庫檔案就在當前目錄,編譯器預設 也不會去找的,所以-l.選項不能少。編譯器缺省會找的目錄可以用-print-search-dirs選項檢視。編譯器會在這些搜尋路徑以及-l選項指定的路徑中查詢用-l選項指定的庫,比如-lstack,編譯器會首先找有沒有共享庫libstack.so,如果有就鏈結它,如果沒有就找有沒有靜態庫libstack.a,如果有就鏈結它。所以編譯器是優先考慮共享庫的,如果希望編譯器只鏈結靜態庫,可以指定-static選項。

回到頂部

組成共享庫的目標檔案和一般的目標檔案有所不同,在編譯時要加-fpic選項,例如:

$ gcc -c -fpic stack/stack.c stack/push.c stack/pop.c stack/is_empty.c
-f後面跟一些編譯選項,pic是其中一種,表示生成位置無關**(position independent code)。

現在把main.c和共享庫編譯鏈結在一起,然後執行:

$ gcc main.c -g -l. -lstack -istack -o main 

$ ./main

./main: error while loading shared libraries: libstack.so: cannot open shared object file: no such file or directory

結果出乎意料,編譯的時候沒問題,由於指定了-l.選項,編譯器可以在當前目錄下找到libstack.so,而執行時卻說找不到libstack.so。那麼執行時在哪些路徑下找共享庫呢?我們先用ldd命令檢視可執行檔案依賴於哪些共享庫:

$ ldd main        

linux-gate.so.1 => (0xb7f5c000)

libstack.so => not found

libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7dcf000)

/lib/ld-linux.so.2 (0xb7f42000)

ldd模擬執行一遍main,在執行過程中做動態鏈結,從而得知這個可執行檔案依賴於哪些共享庫,每個共享庫都在什麼路徑下,載入到程序位址空間的什麼位址。/lib/ld-linux.so.2是動態鏈結器,它的路徑是在編譯鏈結時指定的,gcc在做鏈結時用dynamic-linker指定動態鏈結器的路徑,它也像其它共享庫一樣載入到程序的位址空間中。libc.so.6的路徑/lib/tls/i686/cmov/libc.so.6是由動態鏈結器ld-linux.so.2在做動態鏈結時搜尋到的,而libstack.so的路徑沒有找到。linux-gate.so.1這個共享庫其實並不存在於檔案系統中,它是由核心虛擬出來的共享庫,所以它沒有對應的路徑,它負責處理系統呼叫。總之,共享庫的搜尋路徑由動態鏈結器決定,從ld.so(8)的man page可以查到共享庫路徑的搜尋順序:

首先在環境變數ld_library_path所記錄的路徑中查詢。

然後從快取檔案/etc/ld.so.cache中查詢。這個快取檔案由ldconfig命令讀取配置文 件/etc/ld.so.conf之後生成,稍後詳細解釋。

如果上述步驟都找不到,則到預設的系統路徑中查詢,先是/usr/lib然後是/lib。

先試試第一種方法,在執行main時通過環境變數ld_library_path把當前目錄新增到共享庫的搜尋路徑:

$ ld_library_path=. ./main
這種方法只適合在開發中臨時用一下,通常ld_library_path是不推薦使用的,盡量不要設定這個環境變數,理由可以參考why ld_library_path is bad。

再試試第二種方法,這是最常用的方法。把libstack.so所在目錄的絕對路徑(比如/home/akaedu/somedir)新增到/etc/ld.so.conf中(該檔案中每個路徑佔一行),然後執行ldconfig

$ sudo ldconfig -v
ldconfig命令除了處理/etc/ld.so.conf中配置的目錄之外,還處理一些預設目錄,如/lib、/usr/lib等,處理之後生成/etc/ld.so.cache快取檔案,動態鏈結器就從這個快取中搜尋共享庫。現在再用ldd命令檢視,libstack.so就能找到了:

$ ldd main        

linux-gate.so.1 => (0xb809c000)

libstack.so => /home/akaedu/somedir/libstack.so (0xb806a000)

libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7f0c000)

/lib/ld-linux.so.2 (0xb8082000)

第三種方法就是把libstack.so拷到/usr/lib或/lib目錄,這樣可以確保動態鏈結器能找到這個共享庫。

其實還有第四種方法,在編譯可執行檔案main的時候就把libstack.so的路徑寫死在可執行檔案中:

$ gcc main.c -g -l. -lstack -istack -o main -wl,rpath,/home/akaedu/somedir
-wl,-rpath,/home/akaedu/somedir表示-rpath /home/akaedu/somedir是由gcc傳遞給鏈結器的選項。可以看到readelf的結果多了一條rpath記錄:

$ readelf -a main 

...

dynamic section at offset 0xf10 contains 23 entries:

tag type name/value

0x00000001 (needed) shared library:

[libstack.so]

0x00000001 (needed) shared library: [libc.so.6]

0x0000000f (rpath) library rpath: [/home/akaedu/somedir]

...

$ gcc -o main main.c -g -l. -lstack -istack  ./stack/libstack.so
標籤: c/c++, gcc, 靜態庫, 動態庫

Linux下GCC編譯過程及靜態鏈結庫和動態鏈結庫

一 gcc編譯過程 我們知道gcc是乙個強大的編譯器,很多linux下的gnu工具都是用c語言寫的,並且用gcc編譯的,那麼gcc的編譯過程是怎樣的呢,先來看乙個總的流程圖,我自己簡單畫的,湊合著看 1首先是原始檔經過預載入變成了.i結尾的檔案,可以通過 e這個引數來生成這個中間檔案,這裡主要是把一...

Windows下gcc編譯鏈結

在windows的dos下實現gcc編譯和鏈結 這裡主要看的是兩篇寫的很詳細的文章 c語言多檔案編譯初探 一 c語言多檔案編譯初探 二 3.此時就可以在dos中使用gcc了。gcc可以將c c 檔案編譯為.o檔案,然後鏈結生成可執行檔案.exe。4.接下來我們寫兩個原始檔,乙個標頭檔案,用來模擬多檔...

Linux 下編譯鏈結動靜態庫

linux 版本是 red hat 9 核心版本是 2.4.18 輸入 which gcc 檢視 gcc 的位置在 usr bin gcc gcc v 檢視 gcc 編譯前的配置資訊 prefix usr 說明了安裝目錄 沒有 with headers 說明預設的 include 就在安裝目錄下 所...