日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux动态库符号检查,写 Linux 动态库的最佳实践

發布時間:2023/12/18 linux 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux动态库符号检查,写 Linux 动态库的最佳实践 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在定義全局變量和函數是,如果我們使用 static 關鍵字修飾他們,就只能夠在同一個文件內引用他們;如果我們不使用 static 關鍵字,就可以在其他文件中引用他們。

然而,當實現動態庫時,問題就變得有些復雜。

動態庫的接口函數可以被動態庫內的其他文件引用,也可以被其他動態庫引用。而動態庫的內部函數只能被同一個動態庫內的其他文件引用,不能被其他動態庫引用。

對于“如何讓函數可以被動態庫內的其他文件引用,而不能被其他動態庫引用”的需求,static 關鍵字是無能為力的。

這時,我們就需要修改符號的可見性(visibility)。

符號

對于 ELF 文件來說,程序中出現的所有變量和函數都是符號(symbol)。

變量所在的內存單元和函數的函數體被稱作符號的定義(definition)。

當我們使用 static 關鍵字修飾變量或者函數時,我們是在修改符號的 binding(綁定關系)。在 C 語言中,我們通常稱之為作用域。

符號的 Binding

符號一共有三種 binding,分別是:

binding

含義

LOCAL

本地符號,只能在文件內被引用

GLOBAL

強全局符號,可以被其他文件引用,而且只能在一個文件中被定義

WEAK

弱全局符號,可以被其他文件引用,但是可以在多個文件中被定義

Local Symbol

使用 static 關鍵字修飾的全局變量和函數是 local symbol。

這類符號只能在同一個文件中被引用,而不能被其他文件引用。多個文件可以定義同名的 local 符號,但是這些符號不會互相影響。

一個動態庫中的 local symbol 和另一個動態庫的同名 local symbol 之間不會互相影響。

Global Symbol

不使用 static 關鍵字修飾的全局變量和函數是 global symbol 。

這類符號能在其他文件中被引用,也可以其他動態庫引用。也就是說,這樣的符號在整個進程空間內有唯一的定義。

在鏈接時,如果多個文件中定義了重名的 global 符號,就會引發鏈接錯誤。

在動態加載時,如果多個動態庫定義了重名的 global 符號,那么就只會保留其中的一個定義。這就意味著,在訪問同一個動態庫內定義的 global 符號時,有可能訪問到的是其他動態庫中的定義。

在 ELF 文件層面,在動態庫中訪問 global symbol 都需要借助 PLT 和 GOT,而不能直接訪問,因此速度也比訪問 local symbol 慢。

Weak 符號

在 C 和 C++ 程序中,有以下方法可以定義 weak symbol:

使用 __attribute__((weak)) 修飾的全局變量和函數是 weak symbol;

C++ 庫中的 operator new 和 operator delete 是 weak symbol;

3.如果定義了內聯函數,但是該內聯函數生成了一個獨立的函數體,那么該符號為 weak symbol;

在 C++ 中,在類定義里直接定義的成員函數都自帶 inline 效果,因此也是 weak symbol;

函數模版實例化后的代碼是 weak symbol。

Weak symbol 可以在多個文件中被定義,但是鏈接時只有一個定義會被保留。保留的規則是:

如果有多個同名的 weak symbol,那么符號長度最長的會被保留。

對于變量,就是大小最大的定義會被保留。

對于函數,就是函數體最長的定義會被保留。

如果有多個同名的 weak symbol 和一個 global symbol,那么那個 global symbol 的定義會被保留。

因此,如果用戶定義了 operator new 函數,那么鏈接器就會使用用戶定義的實現,而不是標準庫中的實現。

符號的 Visiblity

為了解決全局符號可能在動態庫之間互相干擾的問題,ELF 引入了符號的可見性(visibility)。

在鏈接成動態庫或者可執行文件時,鏈接器根據符號的 visibility 修改它的 binding。

Visibility 一共有 7 種,但是常用的只有 default 和 hidden 兩種。它們的修飾符分別是:

__attribute__((visibility ("default")))

__attribute__((visibility ("hidden")))

默認的 visibility 是 default,但是可以在編譯時傳入命令行參數 -fvisibility=hidden 將默認 visibility 設置為 hidden。

Default Visibility

在鏈接時,符號的 binding 保持不變。

Visibility 為 default 的 global 符號可能被其他動態庫的同名符號覆蓋,導致在運行時訪問的是其他動態庫中的定義,而非該動態庫內的定義。

通常,需要導出的符號的 visibility 為 default。

Hidden Visibility

這類符號在鏈接成動態庫或者可執行文件后,binding 會從 global 變成 local,同時 visibility 變成 default。

因此,這類符號只能在動態庫內部被訪問,而不能被其他動態庫訪問。

對于動態庫或者可執行程序來說,所有不需要導出的符號的 visibility 都應該是 hidden。

最佳實踐

在實現 C 和 C++ 的動態庫時,使用 -fvisibility=hidden 來編譯動態庫。

在定義 API 時,建議使用 DLL_PUBLIC 和 DLL_LOCAL 宏來控制符號的可見性,它在 Windows、Cygwin、Linux 和 macOS 上都可以正常工作:

#if defined _WIN32 || defined __CYGWIN__

#ifdef BUILDING_DLL

#ifdef __GNUC__

#define DLL_PUBLIC __attribute__ ((dllexport))

#else

// Note: actually gcc seems to also supports this syntax.

#define DLL_PUBLIC __declspec(dllexport)

#endif

#else

#ifdef __GNUC__

#define DLL_PUBLIC __attribute__ ((dllimport))

#else

// Note: actually gcc seems to also supports this syntax.

#define DLL_PUBLIC __declspec(dllimport)

#endif

#define DLL_LOCAL

#endif

#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

在 C 中,可以使用這個宏導出函數和變量:

// 使用 DLL_PUBLIC 修飾需要導出的符號

DLL_PUBLIC int my_exported_api_func();

DLL_PUBLIC int my_exported_api_val;

// 不使用 DLL_PUBLIC 修飾動態庫內部的符號,

// 因為默認可見性被修改為 hidden

int my_internal_global_func();

在 C++ 中,可以使用這個宏來導出一個類:

// 使用 DLL_PUBLIC 修飾需要導出的類

class DLL_PUBLIC MyExportedClass {

public:

// 類里面的所有方法默認都是 DLL_PUBLIC 的

MyExportedClass();

~MyExportedClass();

int my_exported_method();

private:

int c;

// 使用 DLL_LOCAL 修飾動態庫的內部符號

DLL_LOCAL int my_internal_method();

};

參考閱讀

Symbol Table Section ELF 文件中符號表的定義,詳細描述了 binding 與 visibility。

Visibility GCC wiki 中關于 visibility 的最佳實踐。

總結

以上是生活随笔為你收集整理的linux动态库符号检查,写 Linux 动态库的最佳实践的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。