忘记我甚至写过这个问题。
几点说明是为了第一:
- 非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文件和每个库包括有问题的标题 - 因此是双免费的。