LINUX靜態庫與動態庫符號衝突問題分析與解決

2021-10-06 20:39:12 字數 4471 閱讀 7679

1. 問題重現模型

為了重現問題並去掉無關干擾細節,我們將構建乙個最簡單的可執行模組和依賴模組的關係鏈,程式依賴模型如下:

1.1 解釋

(1)有乙個名為rtsp的第三方庫提供了公共介面rtsp_open,rtsp可以編譯為靜態庫librtsp_static.a也可以編譯為動態庫librtsp_shared.so。

(2)基於rtsp庫封裝了乙個名為stream的庫,該庫以動態庫libstream.so的形式提供使用。stream庫提供了1個名為stream_open的介面,該介面在內部使用rtsp庫提供的公共介面rtsp_open。

(3)使用者程式呼叫了stream庫的stream_open介面。

(4)整個依賴鏈關係為使用者程式依賴stream庫,stream庫依賴rtsp庫。

1.2 **

為了便於看到實驗效果,rtsp庫的靜態庫(.a)和動態庫(.so)分別使用不同的原始檔進行編譯,但是他們提供相同的介面。(現實情況是使用同一套**生成靜態庫和動態庫,此處只是為了方便看到實驗效果)。

(1)rtsp庫**如下:

1)rtsp.h

#ifndef __rtsp_h__

#define __rtsp_h__

int rtsp_open();

int rtsp_close();

int rtsp_parse();

#endif12

3456

2)rtsp_static.c

#include "stdio.h"

int rtsp_open()

int rtsp_close()

int rtsp_parse()12

3456

78910

1112

1314

1516

1718

193)rtsp_shared.c

#include "stdio.h"

int rtsp_open()

int rtsp_close()

int rtsp_parse()12

3456

78910

1112

1314

1516

1718

19(2)stream庫**如下:

1)stream.h

#ifndef __stream_h__

#define __stream_h__

int stream_open();

#endif12

342)stream.c

#include "rtsp.h"

int stream_open()12

3456

(3)使用者程式**如下:

1)test_stream.c:

#include "stream.h"

int main(int argc, char *ar**)12

3456

71.3 編譯

各模組編譯語句如下:

(1)librtsp_shared.so

gcc -g -fpic -shared rtsp_shared.c -o 

librtsp_shared.so12

(2)librtsp_static.a

gcc -c -g -fpic rtsp_static.c -o rtsp_static.o

ar crv librtsp_static.a rtsp_static.o12

(3)libstream.so

stream庫使用rtsp靜態庫,請注意stream庫的編譯方法,非常重要,後面解決符號衝突問題會修改此編譯語句!

gcc -g -fpic -shared stream.c -o libstream.so -l./ -lrtsp_static

1(4)使用者程式的編譯放在下一節分析,因為使用者程式的編譯方法不同將導致使用者程式執行結果不同,使用者程式有可能呼叫到rtsp靜態庫中的rtsp_open介面,也有可能呼叫到rtsp動態庫中的rtsp_open介面。

2. 問題重現分析

現在我們再來看下上述**的程式依賴模型圖:

2.1 情景分析

stream庫在內部使用了第三方rtsp庫提供的公共介面,如果使用者程式也直接使用了rtsp庫會出現什麼情況?請看下面的情景分析。

(1)情況1

使用者程式使用如下語句編譯:

gcc -g test_stream.c -o test_stream -l./ -lstream

1程式執行結果如下:

可以看到使用者程式最終呼叫了rtsp靜態庫中的rtsp_open介面。

分析:通常情況下,使用者程式直接使用封裝的stream庫,stream庫隱含的使用了第三方rtsp庫的靜態庫,這個隱含關係對於使用者來說是不可見的。如果使用者程式不直接使用rtsp動態庫,一切都沒有問題。

情況1模組載入關係如下:

圖中藍色部分代表使用者程式執行期間被載入到記憶體中的模組,從圖中可以看到,使用者程式執行時只存在rtsp靜態庫,因此rtsp_open是唯一的。

(2)情況2

使用者程式使用如下語句編譯:

gcc -g test_stream.c -o test_stream -l./ -lrtsp_shared -lstream

1程式執行結果如下:

可以看到使用者程式最終呼叫了rtsp動態庫中的rtsp_open介面。

分析:在這種情況下,使用者程式使用了stream庫,同時又直接鏈結了第三方rtsp庫的動態庫(編譯語句中的紅色部分)。因為stream庫是使用rtsp庫的靜態庫(librtsp_static.a)編譯的,現在又鏈結了rtsp庫的動態庫(librtsp_shared.so),因此在使用者程式中會有兩個相同的rtsp_open符號,載入器在載入應用程式並繫結符號時就要做出決議,到底是要使用靜態庫中的rtsp_open符號還是動態庫中的rtsp_open符號。

從執行結果上來看,上述的編譯語句編譯出來的使用者程式使用了動態庫中的rtsp_open符號。

情況2模組載入關係如下:

圖中藍色部分代表使用者程式執行期間被載入到記憶體中的模組,從圖中可以看到,使用者程式執行時同時存在rtsp靜態庫和rtsp動態庫,因此rtsp_open具有二義性。

實際上,即使使用者使用了rtsp動態庫也不一定會導致使用者程式呼叫到動態庫中的rtsp_open符號。例如,我們把上面的編譯語句改為下面的:

原來的使用者程式編譯語句:

gcc -g test_stream.c -o test_stream -l./ -lrtsp_shared –lstream

1修改的使用者程式編譯語句:

gcc -g test_stream.c -o test_stream -l./ -lstream -lrtsp_shared

1重新編譯編譯後執行使用者程式,輸出如下:

可以看到即使鏈結了rtsp動態庫,使用者程式最終還是呼叫了靜態庫中的rtsp_open介面。

具體原因在此處暫時不展開,但是可以說明一點,如果使用者程式使用了rtsp動態庫可能會產生符號衝突問題,並且這個行為是stream庫提供者不能控制的!

3. 問題解決方案

3.1 解決方法

我們回過頭來看一下stream庫的編譯語句:

gcc -g -fpic -shared stream.c -o libstream.so -l./ -lrtsp_static

1再看一下libstream.so的重定位資訊:

可以發現rtsp_open是乙個動態繫結符號,所謂動態繫結符號就是編譯鏈結階段並不確定符號位址,符號位址的解析和繫結推遲到裝載階段。

因此解決問題的一種思路就是在編譯鏈結階段將使用的rtsp靜態庫中的符號位址確定下來。

解決的辦法就是在編譯libstream.so的時候加上-wl,-bsymbolic編譯選項,該編譯選項的含義是在鏈結過程中優先使用本模組內部的符號。

原來的libstream.so編譯語句:

gcc -g -fpic -shared stream.c -o libstream.so -l./ -lrtsp_static

1修改的libstream.so編譯語句:

gcc -g -fpic -shared stream.c -o libstream.so -l./ -lrtsp_static -wl,-bsymbolic

1重新編譯libstream.so後檢視重定位資訊:

可以看到libstream.so中的動態繫結符號中已經沒有rtsp_open這個符號了。

3.2 驗證

重新編譯使用者程式並使用新的libstream.so,驗證符號衝突問題是否解決:

(1)gcc -g test_stream.c -o test_stream -l./ -lrtsp_shared –lstream

1執行結果:

(2)gcc -g test_stream.c -o test_stream -l./ -lstream -lrtsp_shared

1執行結果:

從上面2個例子可以看出,使用者程式載入了動態庫librtsp_shared.so,但是使用的都是rtsp靜態庫(librtsp_static.a)中提供的rtsp_open介面,符號衝突問題已經解決。

Linux靜態庫與動態庫

靜態庫 a 靜態庫的 在編譯過程中已經被載入可執行程式,因此體積較大。編譯程式時候需要庫作依賴,執行時候不需要。方便,不再需要外部函式庫支援 缺點 1 因為靜態庫被鏈結後直接嵌入可執行程式中,相當於每乙個可執行程式裡都有乙個庫的副本,浪費空間 2 一旦庫中有bug,需要重新編譯。建立步驟 1 編寫函...

linux動態庫與靜態庫

現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的 都從零開始。盡量不重複做別人已經做過的事,站在巨人的肩膀上 做事情。根據鏈結時期的不同,庫又有 靜態庫和共享庫 動態庫 二者的不同點在於 被載入的時刻不同,靜態庫的 在編譯過程中已經被載入可執行程式,因此體積較大。共享庫的 是在可執行程式執行時...

Linux 靜態庫與動態(共享)庫

不論是在linux還是windows下程式設計,我們都會用到庫,有自身帶的標準庫,也有我們自己寫的庫,庫就是預先編譯好的的方法的集合。linux中的庫可以分為兩種,靜態庫和動態庫,動態庫也稱為共享庫。在linux中,庫名稱都以lib開始,靜態庫名為 lib a,動態庫名為 lib so。靜態庫和動態...