查看so库中是否有某个定义_论Linux ELF中动态库符号重定义利用 属性/Linker 做隐藏的手法...
假如libgetthree.so libgetseven.so , 同時這兩個so內部都用了internal_do_calculation()函數,并且各自定義了自己的internal_do_calculation()的實現,你會想當然的認為他們各自不影響,libgetthree和libgetseven會分別用自己的internal_do_calculation(),但事與愿違,你會發現都只會用其中一個so的符號。
他經歷的過程如下:
于是你會發現,libgetseven.so用的是其他庫的這個符號,錯誤就產生了。取決于你怎么鏈接順序,經過測試,發現跟第一個庫的順序有關
但是如果我們一旦隱藏比如three.cpp的符號
我們再重新鏈接,發現就正常了
可以總結出如果你對某個so庫中的符號做了hidden,他實際上有兩個作用:
通過上面這兩個效果,你可以發現,無論你是hidden libgetthree.so還是libgetseven.so都是work的。
除了以上的好處之外,如果你控制得體,比如全局使用-fvisibility=hidden,對對應的public API使用__attribute__((visibility("default")))來暴露,這樣有以下額外的好處:
壞處:
他會讓你做單元測試更加困難,因為你已經把你的內部實現符號都給隱藏了,因此當你做unit testing的時候,你需要用default visibility來重新build.你可以借此來重新配置你的debug / release build flags:
Debug:
- -g - 加上debug info
- -O0 - 不提供任何優化(可以提供在開發階段的debug使用體驗)
Release:
- -fvisibility=hidden - 上面說的,可以提高效率
- -O2 - 優化大小和提高速度
有一個需要注意的地方就是關于異常C++ Exceptions,當binary code捕獲住了一個exception的時候,他需要typeinfo的查找,但是typeinfo的symbols會隨著你本身symbols的hidden而hidden.
通過linker的-Bsymbolic / -Bsymbolic-functions同樣可以解決上面的問題(gcc是要加上-Wl,-Bsymbolic),比如你可以用如下的命令來進行編譯
他會帶來正確的結果:
但是如果那他跟hidden visibility做比較的話還是有諸多缺點:
除了以上提供的方法之外,你還能利用-fvisibilty=protected來達到效果,他跟hidden類似,可以保護你當前shared library的庫的符號不會被其他庫方便,但是他不保證你的庫會去污染其他庫的符號表,比如我們來看例子:
這個情況下是OK的,因為我們保護了Seven本身,因此他的內部符號只會用自己的
我們來看另外一個例子
這就出問題了,因為我們雖然保護了three本身,但是他的符號也確實污染到了libseven.so,因此輸出了3
你也可以通過匿名空間來達到效果
注意不要把你的public API也給包了,僅僅包你的實現,然后你通過nm查看發現他們默認變成小t了
但是如果你不用匿名空間,而是帶名字的空間,他是大T的會做污染
因此可以看到匿名空間的一個作用就是自動幫你把符號隱藏. 同樣,如果你的函數定義成static靜態的,你的函數符號默認也是hidden的,也能起到同樣的效果
Dynamic Linking解決相關問題:
試想一下如果你的library A和B都對C有依賴,其中A用的是新的C,B用的是老的C。這里面可能存在數據結構的不一致,就會出現問題,但是正是因為有了dynamic linking,如果A和B對于major version的C是兼容的,那么dynamic linking會幫你解決這個問題,因為他們所有遇到過的C的符號通過上面的解釋,都會保持一致,因為有override的行為在里面。 但是并不是說dynamic linking就是萬能解藥,靜態編譯static linking的一個原因就是擁有盡可能小的分發依賴。 另外一個原因就是你要測試所有版本的依賴非常困難,通過靜態編譯到一個特定的版本允許讓你擁有這個依賴的一致行為
總結
以上是生活随笔為你收集整理的查看so库中是否有某个定义_论Linux ELF中动态库符号重定义利用 属性/Linker 做隐藏的手法...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos vsftp mysql_C
- 下一篇: Android多线程优劣,Android