GCC的符號可見性 解決多個庫同名符號衝突問題

2021-07-11 14:46:59 字數 3865 閱讀 5585

問題

最近專案遇到一些問題,場景如下

主程式依賴了兩個庫liba的funca函式和libb的funcb函式。示意的**(main.cpp)如下:

#include 

int funca(int, int);

int funcb(int, int);

int main()

liba示意實現(liba.cpp)如下:

int subfunc(int a, int b) 

int funca(int a, int b)

libb示意實現(libb.cpp)如下:

int subfunc(int a, int b) 

int funcb(int a, int b)

可見funca呼叫了liba中的內部函式subfunc,funcb呼叫了libb中的內部函式subfunc,這兩個subfunc實現不同,但不幸的是名字不小心起得一樣了

這時我們嘗試編譯並執行:

g++ -fpic liba.cpp -shared -o liba.so

g++ -fpic libb.cpp -shared -o libb.so

g++ main.cpp liba.so libb.so -o main

export ld_library_path=.

./main

我們期望的結果是3,1(funca和funcb各自呼叫不同的subfunc實現),

實際得到的結果是3,3(funca和funcb都呼叫了liba中的subfunc實現)原因

我們通過readelf來檢視符號:

$ readelf -a liba.so | grep subfunc

000000200a60 000200000007 r_x86_64_jump_slo 0000000000000708 _z7subfuncii + 0

2: 0000000000000708

20 func global

default

10 _z7subfuncii

45: 0000000000000708

20 func global

default

10 _z7subfuncii

$ readelf -a libb.so | grep subfunc

000000200a60 000200000007 r_x86_64_jump_slo 0000000000000708 _z7subfuncii + 0

2: 0000000000000708

22 func global

default

10 _z7subfuncii

45: 0000000000000708

22 func global

default

10 _z7subfuncii

可見liba和libb裡面都有subfunc符號,名字完全一樣,而且都是global的

global的符號即全域性的符號,同名的全域性符號會被認為是同乙個符號,由於main先載入了liba,得到了liba中的subfunc符號,再載入libb時,就把libb中的subfunc忽略了。

解決方案

這其實是符號的可見性(symbol visibility)問題,既然有global符號,那自然會有local符號,local的符號只在當前lib可見,全域性不可見。

如何將符號變成local的呢,最直接的就是加上visibility為hidden的標誌,修改後的liba.cpp:

__attribute__ ((visibility ("hidden"))) int subfunc(int a, int b) 

int funca(int a, int b)

再重新編譯執行,可以得到結果為3,1,成功!這裡再檢視一下liba的符號:

$ readelf -a liba.so | grep subfunc

40:00000000000006a8 20

func

local

default

10 _z7subfuncii

可見subfunc符號已經變成了local

預設local

上面的方法可以解決問題,但是,實際情況往往是,liba裡面有很多的內部函式,而暴露給外部的只有少數,能不能指定少數符號為global,其它的都是local呢?答案是肯定的,修改liba.cpp如下:

int subfunc(int a, int b) 

__attribute__ ((visibility ("default"))) int funca(int a, int b)

這時,liba的編譯引數需要加上-fvisibility=hidden:

g++ -fpic liba.cpp -shared -fvisibility=hidden -o liba.so
同樣可以解決問題。

跨平台相容性

windows平台對於符號的行為是不一樣的,windows預設動態庫里符號是local的,通過__declspec(dllexport)來宣告global符號,所以可以用下面的方式來相容:

#if defined _win32 || defined __cygwin__

#ifdef building_dll

#ifdef __gnuc__

#define dll_public __attribute__ ((dllexport))

#else

#define dll_public __declspec(dllexport) // note: actually gcc seems to also supports this syntax.

#endif

#else

#ifdef __gnuc__

#define dll_public __attribute__ ((dllimport))

#else

#define dll_public __declspec(dllimport) // note: actually gcc seems to also supports this syntax.

#endif

#endif

#define dll_local

#else

#if __gnuc__ >= 4

#define dll_public __attribute__ ((visibility ("default")))

#define dll_local __attribute__ ((visibility ("hidden")))

#else

#define dll_public

#define dll_local

#endif

#endif

隱藏外部依賴的符號

我遇到的實際情況比上面更複雜一些,subfunc並不是在liba中實現的,而是在另乙個外部庫libsubfunc.a中實現的。liba通過包含標頭檔案來獲取到這個函式:

#include "subfunc.h"

int funca(int a, int b)

上面的-fvisibility僅對實現生效,不能對宣告生效。但libsubfunc.a是第三方庫,我們不能去改它的**,也不能改它的標頭檔案,對於這種情況,gcc提供了下面方式來支援:

#pragma gcc visibility push(hidden)

#include "subfunc.h"

#pragma gcc visibility pop

int funca(int a, int b)

這種方式更方便靈活。

如果是dlopen載入so可以通過引數控制。

控制符號的可見性

在普通的c語言中,如果您希望將函式或者變數限制在當前檔案中,需要對其使用static關鍵字。然而,在乙個包含很多檔案的共享庫中,如果您希望某個符號可以被共享庫內部的幾個檔案訪問,而又不提供給外部,則對符號進行隱藏處理就會比較困難。大多數的聯結器都提供一些便利的方法來隱藏或者顯示模組中所有的符號,但如...

C C 中控制動態庫的符號可見性

寫了乙個動態庫供客戶使用,此庫使用了一些第三方靜態庫,無奈客戶也使用了這些第三方庫,從而產生了符號衝突。所以需要隱藏此庫中第三方庫的匯出符號。static 關鍵字 匯出列表 指定 visibility 屬性 最終選擇方案三,即利用gnu的visibility 屬性。1 修改cmakelist.txt...

Oracle的多個資料庫的聯合查詢 包含同義詞

如果需要同時對2個資料庫的多個表進行關聯查詢操作.1 需要建立乙個database links 2 通過這樣查詢 select from table name dblinks name 3 如果對應的表需要通過這樣訪問 dbuser.table name來訪問,則可以通過建立同義詞,來省略表名前面的...