这个问题与this one及其答案有关。
我刚刚在我正在处理的构建中发现了一些丑陋。情况看起来有点像(以 gmake 格式编写);注意,这特别适用于 sparc 和 x86 硬件上的 32 位内存模型:
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 了解不够,不知道它是否是一个好主意 / 聪明,我的猜测是在这种情况下,它不需要,而是它正在发生,因为有人没有足够的关心,以找出正确的方式来做它,当新的东西添加到构建时。
我的问题是:
这是安全的
这是个好主意吗
可能导致哪些潜在问题
如果我把一切都切换到 PIC,是否有任何不明显的陷阱,我可能要注意。
忘了我甚至写了这个问题。
首先进行一些解释:
非 PIC 代码可以由操作系统加载到 [大多数?] 现代操作系统的内存中的任何位置。加载所有内容后,它将经历一个修复文本段(可执行内容结束的位置)的阶段,因此它正确地解决了全局变量;为了实现这一点,文本段必须是可写的。
代码被编译为使用全局偏移表 (GOT),因此其可以相对于 GOT 寻址全局,从而减少了对修复的需求。
如果共享对象是在没有 PIC 的情况下构建的,虽然强烈鼓励它似乎并不是严格必要的;如果操作系统必须修复文本段,那么它将其加载到标记为读写的内存中......这可以防止跨进程 / 用户共享。
如果一个可执行的二进制文件是用 / PIC 构建的,我不知道引擎盖下出了什么问题,但我目睹了一些工具变得不稳定(神秘的崩溃等)。
答案:
混合 PIC / 非 PIC,或在可执行文件中使用 PIC 可能会导致难以和跟踪不稳定性。
...包括 segfaults,总线错误,堆栈损坏,以及可能更多。
共享对象中的非 PIC 可能不会导致任何严重的问题,但如果跨进程和 / 或用户多次使用库,可能会导致使用更多的 RAM。
update (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 和包含违规标头的每个库运行一次构造函数 / 析构函数-因此是双重自由。
本站系公益性非盈利分享网址,本文来自用户投稿,不代表码文网立场,如若转载,请注明出处
评论列表(70条)