忘記我甚至寫過這個問題。
幾點說明是爲了第一:
- 非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文件和每個庫包括有問題的標題 - 因此是雙免費的。