[英]How -fvisibility-inlines-hidden differs from -fvisibility=hidden in gcc
[英]Why `-fvisibility-inlines-hidden` is not the default?
我想看看我的理解是否正確。
inline
是對C ++編譯器的建議,用於在函數看到更好時替換函數,因此調用標記為從庫外部內聯的過程不應該是可靠的,並且它們在邏輯上應該默認隱藏,防止其他人將它們稱為更新到編譯器或代碼庫可以改變決定(從而刪除inline
d函數和ABI破壞?)。
但是,它似乎不是默認設置,並且應設置-fvisibility-inlines-hidden
以使其發生。 我問這里為什么會這樣? 沒有設置任何真實的用例,它只是因為遺留的原因?
它們在邏輯上應默認隱藏
C ++要求所有函數(包括inline
函數)在所有翻譯單元中具有相同的地址。 所有翻譯單位也應共享本地靜態。 如果將程序構建為多個共享對象(.so文件),則隱藏內聯函數會違反這些要求。
內聯函數應具有公共可見性,以便動態鏈接器可以在運行時從所有現有選項中選擇一個定義。
GCC 維基提到:
-fvisibility-inlines-hidden
可以在沒有源更改的情況下使用, 除非您需要覆蓋地址標識對於函數本身或任何函數本地靜態數據很重要的內聯 。
考慮以下示例。 可執行來源:
// main.cpp
#include <cstdio>
struct A { };
inline A &foo()
{
static A a;
return a;
}
int main()
{
void *p = &foo();
std::printf("main() - %p\n", p);
}
共享對象源:
#include <cstdio>
struct A { };
inline A &foo()
{
static A a;
return a;
}
static __attribute__((constructor)) void init()
{
void *p = &foo();
std::printf("main() - %p\n", p);
}
如果您針對此共享對象構建了兩者並鏈接可執行文件,則可以看到foo
始終在兩個翻譯單元中返回相同的地址。
現在,如果將__attribute__((visibility("hidden")))
到這些內聯函數,那么您將看到不同翻譯單元中的地址不同。
這不是某些C ++程序可能期望的。
今天大多數人認為inline
與實際函數內聯無關。 這不完全正確。 ELF目標嘗試使動態鏈接透明,例如,如果程序構建為單個可執行文件或多個共享對象,則程序應該具有相同的行為。
為了使ELF成為可能,需要通過GOT或PLT調用所有具有公共可見性的功能,就好像它是“導入”功能一樣。 這是必需的,以便每個函數都可以被另一個庫(或可執行文件本身)覆蓋。 這也禁止內聯所有公共非內聯函數(參見3.5.5節,其中顯示PIC中的公共函數調用應通過PLT進行)。
公共內聯函數的代碼內聯是可能的,因為當內聯函數的多個定義不相等時,內inline
允許程序行為未定義。
有趣的說明:Clang違反了ELF的要求,無論如何都能夠在ELF目標上內聯公共功能。 GCC可以使用-fno-semantic-interposition
標志執行相同的操作。
inline
是對C ++編譯器的建議,用於在函數看到更好時替換它
不是。最初的情況可能是90年代后期,但很長一段時間都沒有。
請參閱此答案以獲得更好的解釋。
因此,從庫外部調用標記為內聯的過程應該不可靠
即使編譯器內聯調用(它可能有或沒有做inline
關鍵字),這是在一個特定調用位置完成。 內聯不是函數發生的事情,必須始終像往常一樣發出,而是函數調用 。
編譯器完全可以內聯對函數的一些調用而不是其他調用,這取決於它對將在調用站點產生最佳代碼的意見。
現在,目前還不清楚你認為圖書館中的inline
原因是什么問題,因此很難直接解決這個問題。
inline是對C ++編譯器的建議,用於在函數看到更好時替換函數,因此調用標記為從庫外部內聯的過程不應該是可靠的並且它們在邏輯上應該默認隱藏
編譯器仍然可以決定內聯一些調用,並使其中一些調用無內聯。 在這種情況下,您將在鏈接在一起的所有庫中獲得內聯函數的多個副本。
此外,標准有點要求&foo
在你的程序中到處都是相同的地址,盡管標准沒有提及DSO / DLL的任何內容,因此編譯器在這些方面有一些自由(事實上,MSVC遵循相反的方法留下一切默認隱藏)。
但是,它似乎不是默認設置,並且應設置-fvisibility-inlines-hidden以使其發生。
盡管名稱, -fvisibility-inlines-hidden
僅影響內聯類成員函數。 對於其他一切, -fvisibility=hidden
應該足夠了。
沒有設置任何真實的用例,它只是因為遺留的原因?
是。 IIRC旗幟在GCC 3.2中實施(或足夠接近); 將其設為默認會破壞許多遺留代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.