2011-11-30 71 views
6

此問題與this one以及其答案有關。在共享庫中混合PIC和非PIC對象

我剛剛發現我正在構建的構建中存在一些醜陋現象。這種情況看起來有些像下面這樣(用gmake格式寫成);注意,這特別適用於32位的內存模型SPARC和x86硬件:

OBJ_SET1 := some objects 
OBJ_SET2 := some objects 

# note: OBJ_SET2 doesn't get this flag 
${OBJ_SET1} : CCFLAGS += -PIC 

${OBJ_SET1} ${OBJ_SET2} : %.o : %.cc 
    ${CCC} ${CCFLAGS} -m32 -o ${@} -c ${<} 

obj1.o  : ${OBJ_SET1} 
obj2.o  : ${OBJ_SET2} 
sharedlib.so : obj1.o obj2.o 
obj1.o obj2.o sharedlib.so : 
    ${LINK} ${LDFLAGS} -m32 -PIC -o ${@} ${^} 

顯然它可以工作在共享對象中混合使用和不使用PIC編譯對象(這已經使用多年) 。我不太瞭解PIC知道這是否是一個好主意/聰明,我的猜測是在這種情況下不需要,而是發生這種情況,因爲有人沒有足夠的關注找到正確的方法來做到這一點在新的東西建設。

我的問題是:

  1. 這是安全
  2. 這是個好主意
  3. 可能會出現的結果
  4. 什麼潛在的問題,如果我切換一切PIC,是否有任何非我可能想要留意的明顯陷阱。

回答

4

忘記我甚至寫過這個問題。

幾點說明是爲了第一:

  • 非PIC碼可以由OS在[?大多數]現代操作系統加載到存儲器中的任何位置。在加載完所有內容之後,它會經歷一個修復文本段(可執行文件最終結束)的階段,以便正確解決全局變量;爲了解決這個問題,文本段必須是可寫的。
  • PIC可執行數據可由OS加載一次,並在多個用戶/進程間共享。但是,對於操作系統來說,文本段必須是隻讀的,這意味着不需要修改。該代碼被編譯爲使用全局偏移表(GOT),因此它可以解決相對於GOT的全局問題,從而減少對修復的需求。
  • 如果一個共享對象是在沒有PIC的情況下構建的,儘管強烈建議它不會出現它是絕對必要的;如果操作系統必須修復文本段,那麼它將被迫將其加載到標記爲可讀寫的內存中,這會阻止跨進程/用戶共享。
  • 如果一個可執行的二進制文件是用/ PIC編譯的,我不知道引擎蓋下出了什麼問題,但我目睹了一些工具變得不穩定(神祕的崩潰&之類)。

答案:

  • 混合PIC /非PIC,或使用PIC的可執行文件會導致很難預測和跟蹤不穩定。我沒有技術解釋爲什麼。
    • ...包括段錯誤,總線錯誤,堆棧損壞,可能還有更多。
  • 共享對象中的非PIC可能不會導致任何嚴重問題,但如果庫在進程和/或用戶中多次使用,則會導致使用更多的RAM。

更新(4/17)

因爲我已經發現了一些以前看到崩潰的原因。爲了說明:

/*header.h*/ 
#include <map> 
typedef std::map<std::string,std::string> StringMap; 
StringMap asdf; 

/*file1.cc*/ 
#include "header.h" 

/*file2.cc*/ 
#include "header.h" 

int main(int argc, char** argv) { 
    for(int ii = 0; ii < argc; ++ii) { 
    asdf[argv[ii]] = argv[ii]; 
    } 

    return 0; 
} 

...然後:

$ g++ file1.cc -shared -PIC -o libblah1.so 
$ g++ file1.cc -shared -PIC -o libblah2.so 
$ g++ file1.cc -shared -PIC -o libblah3.so 
$ g++ file1.cc -shared -PIC -o libblah4.so 
$ g++ file1.cc -shared -PIC -o libblah5.so 

$ g++ -zmuldefs file2.cc -Wl,-{L,R}$(pwd) -lblah{1..5} -o fdsa 
#  ^^^^^^^^^ 
#  This is the evil that made it possible 
$ args=(this is the song that never ends); 
$ eval ./fdsa $(for i in {1..100}; do echo -n ${args[*]}; done) 

這個特別的例子可能最終不會崩潰,但它基本上是曾在該組的代碼存在的狀況。如果它確實崩潰它可能會在析構函數中,通常是一個雙免費的錯誤。

很多年前,他們增加了-zmuldefs到他們的構建以擺脫多重定義的符號錯誤。編譯器發出用於在全局對象上運行構造函數/析構函數的代碼。 -zmuldefs強制他們住在內存中的相同位置,但它仍然運行構造函數/析構函數一次爲exe文件和每個庫包括有問題的標題 - 因此是雙免費的。