簡體   English   中英

GCC -fPIC 選項

[英]GCC -fPIC option

我已閱讀GCC 的代碼生成約定選項,但無法理解“生成與位置無關的代碼 (PIC)”的作用。 請舉一個例子來解釋我是什么意思。

位置無關代碼意味着生成的機器代碼不依賴於位於特定地址才能工作。

例如,跳躍將產生為相對而非絕對。

偽組裝:

PIC:無論代碼是在地址 100 還是 1000,這都會起作用

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP

非 PIC:僅當代碼位於地址 100 時才有效

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP

編輯:回應評論。

如果您的代碼是使用 -fPIC 編譯的,則它適合包含在庫中 - 庫必須能夠從其在內存中的首選位置重定位到另一個地址,在您的庫喜歡的地址處可能有另一個已加載的庫。

我將嘗試以更簡單的方式解釋已經說過的內容。

每當加載共享庫時,加載程序(操作系統上加載您運行的任何程序的代碼)都會根據對象加載到的位置更改代碼中的某些地址。

在上面的例子中,非PIC代碼中的“111”是加載器第一次加載時寫入的。

對於非共享對象,您可能希望它是這樣的,因為編譯器可以對該代碼進行一些優化。

對於共享對象,如果另一個進程想要“鏈接”到該代碼,它必須將其讀取到相同的虛擬地址,否則“111”將毫無意義。 但是該虛擬空間可能已經在第二個過程中使用。

內置到共享庫中的代碼通常應該是與位置無關的代碼,以便共享庫可以輕松地(或多或少)加載到內存中的任何地址。 -fPIC選項確保 GCC 生成這樣的代碼。

進一步添加...

每個進程都有相同的虛擬地址空間(如果在 linux OS 中使用標志停止虛擬地址的隨機化)(有關更多詳細信息,請僅為我自己禁用和重新啟用地址空間布局隨機化

因此,如果它的一個 exe 沒有共享鏈接(假設場景),那么我們總是可以將相同的虛擬地址提供給相同的 asm 指令而不會造成任何傷害。

但是當我們想將共享對象鏈接到 exe 時,我們不確定分配給共享對象的起始地址,因為它取決於共享對象的鏈接順序。也就是說,.so 中的 asm 指令總是有不同的虛擬地址取決於它鏈接到的進程。

因此,一個進程可以在自己的虛擬空間中將 .so 的起始地址作為 0x45678910 提供,而其他進程同時可以提供 0x12131415 的起始地址,如果它們不使用相對尋址,則 .so 將根本不起作用。

所以他們總是必須使用相對尋址模式,因此必須使用 fpic 選項。

在加載庫時或在運行時解析動態庫中函數的鏈接。 因此,程序運行時,可執行文件和動態庫都會被加載到內存中。 加載動態庫的內存地址無法提前確定,因為固定地址可能與另一個需要相同地址的動態庫發生沖突。


有兩種常用的方法來處理這個問題:

1.搬遷。 如有必要,修改代碼中的所有指針和地址以適合實際加載地址。 重定位由鏈接器和加載器完成。

2.位置無關代碼。 代碼中的所有地址都是相對於當前位置的。 默認情況下,類 Unix 系統中的共享對象使用與位置無關的代碼。 如果程序運行很長時間,特別是在 32 位模式下,這比重定位效率低。


位置無關代碼”這個名稱實際上意味着以下內容:

  • 代碼段不包含需要重定位的絕對地址,而只有自身相對地址。 因此,代碼段可以加載到任意內存地址並在多個進程之間共享。

  • 數據部分不在多個進程之間共享,因為它通常包含可寫數據。 因此,數據部分可能包含需要重定位的指針或地址。

  • 所有公共功能和公共數據都可以在 Linux 中被覆蓋。 如果 main 可執行文件中的函數與共享對象中的函數同名,則 main 中的版本將優先,不僅在從 main 調用時,而且在從共享對象調用時。 同樣,當 main 中的全局變量與共享對象中的全局變量同名時,即使從共享對象訪問,也會使用 main 中的實例。 這種所謂的符號插入旨在模仿靜態庫的行為。


共享對象有一個指向其函數的指針表,稱為過程鏈接表 (PLT),以及一個指向其變量的指針表,稱為全局偏移表 (GOT),以實現此“覆蓋”功能。

所有對函數和公共變量的訪問都通過這些表。

ps 在無法避免動態鏈接的情況下,有多種方法可以避免位置無關代碼的耗時特性。

您可以從這篇文章中了解更多信息: http ://www.agner.org/optimize/optimizing_cpp.pdf

對已經發布的答案的一個小補充:未編譯為與位置無關的目標文件是可重定位的; 它們包含重定位表條目。

這些條目允許加載程序(將程序加載到內存中的代碼位)重寫絕對地址以調整虛擬地址空間中的實際加載地址。

操作系統將嘗試與鏈接到同一共享對象庫的所有程序共享加載到內存中的“共享對象庫”的單個副本。

由於代碼地址空間(與數據空間的部分不同)不需要是連續的,並且由於鏈接到特定庫的大多數程序都有相當固定的庫依賴樹,因此大多數情況下都會成功。 在極少數存在差異的情況下,是的,可能需要在內存中擁有兩個或多個共享對象庫的副本。

顯然,任何在程序和/或程序實例之間隨機化庫的加載地址的嘗試(以減少創建可利用模式的可能性)都會使這種情況變得普遍,而不是罕見,因此如果系統啟用了此功能,應該盡一切努力將所有共享對象庫編譯為與位置無關。

由於從主程序主體對這些庫的調用也將成為可重定位的,這使得必須復制共享庫的可能性大大降低。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM