Linux下生成动态链接库是否必须使用 -fPIC 的问题
From: http://www.linuxidc.com/Linux/2011-06/37268.htm
?
在 Linux 下制作動態(tài)鏈接庫,“標(biāo)準(zhǔn)” 的做法是編譯成位置無關(guān)代碼(Position Independent Code,PIC),然后鏈接成一個動態(tài)鏈接庫。經(jīng)常遇到的一個問題是 -fPIC 是不是必需,因?yàn)楹孟癫患咏?jīng)常也能正常運(yùn)行,只是創(chuàng)建 .so 的時候會有一個警告。
搜索、試驗(yàn)了一下,答案似乎是這樣:
(1) 通常的建議是始終加上 -fPIC 生成位置無關(guān)代碼;
(2) AMD64 下,必須使用位置無關(guān)代碼,否則連接失敗:
relocation R_X86_64_32S against `a local symbol' can not be used when making a shared object; recompile with -fPIC
(3) IA32 下,連接成功,但有警告:
warning: creating a DT_TEXTREL in object.
這樣的 .so 文件可以完全正常工作。
可執(zhí)行文件在鏈接時就知道每一行代碼、每一個變量會被放到線性地址空間的什么位置,因此這些地址可以都作為常數(shù)寫到代碼里面。對動態(tài)庫,這就不行了,這要等到加載時才知道。無非下面兩種方法:
(1) 可重定位代碼(relocatable code):Windows DLL 以及不使用 -fPIC 的 Linux SO。
生成動態(tài)庫時假定它被加載在地址 0 處。加載時它會被加載到一個地址(base),這時要進(jìn)行一次重定位(relocation),把代碼、數(shù)據(jù)段中所有的地址加上這個 base 的值。這樣代碼運(yùn)行時就能使用正確的地址了。
(2) 位置無關(guān)代碼(position independent code):使用 -fPIC 的 Linux SO。
這樣的代碼本身就能被放到線性地址空間的任意位置,無需修改就能正確執(zhí)行。通常的方法是獲取指令指針(如 IA32 的 EIP 寄存器)的值,加上一個偏移得到全局變量/函數(shù)的地址。
PIC vs. relocatable:
(1) PIC 的缺點(diǎn)主要就是代碼有可能長一些。例如 IA32,由于不能直接使用 [EIP+constant] 這樣的尋址方式,甚至不能直接將 EIP 的值交給其他寄存器,要用到 GOT(global offset table)來定位全局變量和函數(shù)。這樣導(dǎo)致代碼的效率略低。
(2) PIC 的加載速度稍快,因?yàn)椴恍枰鲋囟ㄎ弧?/p>
(3) 多個進(jìn)程引用同一個 PIC 動態(tài)庫時,可以共用內(nèi)存。這一個庫在不同進(jìn)程中的虛擬地址不同,但操作系統(tǒng)顯然會把它們映射到同一塊物理內(nèi)存上。對于可重定位代碼,則必須為每個庫都在物理內(nèi)存中復(fù)制一份副本,因?yàn)樾枰薷钠渲械牡刂贰.?dāng)然,主流現(xiàn)代操作系統(tǒng)都啟用了分頁內(nèi)存機(jī)制,這使得重定位時可以使用 COW(copy on write)來節(jié)省內(nèi)存(32 位 Windows 就是這樣做的);然而,頁面的粒度還是比較大的(例如 IA32 上是 4KiB),至少對于代碼段來說能節(jié)省的相當(dāng)有限。
注:對于 AMD64,由于 AMD64 實(shí)現(xiàn)了 [RIP+constant] 的尋址方式,第 (1) 點(diǎn)不成立。
這樣,把動態(tài)庫編譯成 PIC 只有好處沒有壞處,因而 Linux AMD64 要求用于生成動態(tài)庫的目標(biāo)文件必須使用 -fPIC 編譯也合情合理了。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的Linux下生成动态链接库是否必须使用 -fPIC 的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 他毕业两年,博客一年,时间
- 下一篇: linux简介及安装使用