我知道 snipersim 不是一个非常典型的“项目”,但这更多的是一个 linux/链接问题而不是其他问题,所以我认为它就在这里。我也联系了开发商,但尚未得到答复。
首先,快速解释一下我正在尝试做的事情:
在我的硕士论文中,我使用建筑模拟器 snipersim (http://www.snipersim.org)。我将其下载到本地计算机(运行 Linux Mint 17.2),构建它并开始使用它。一切都工作得很好。
由于我需要进行数百次模拟,每次模拟都需要几个小时,因此我获得了在 x86_64 OpenSUSE 13.1 机器上使用 HTCondor 访问大学计算集群的权限。显然,我没有集群的 root 访问权限。
由于它们具有不同的发行版,我不能简单地复制二进制文件(我后来尝试了,但代码行为不稳定),所以我想重新编译 snipersim。
我的编译过程
在集群的访问机器中(您可以使用condor_submit从其中提交并行作业),我克隆了我的snipersim fork。
SQLite3
我检查了是否安装了必要的库,发现缺少 libsqlite3。为了解决这个问题,我从 SQLite.org 网站下载了 sqlite-autoconf-3090200.tar.gz,将其配置为安装到 ~/sqlite (./configure -prefix=~/sqlite),然后执行了make && make install
.
然后我配置SQLITE_PATH
为指向~/sqlite
,并且 和LIBRARY_PATH
都LD_LIBRARY_PATH
指向~/sqlite/lib
到目前为止,一切都很好。
(为了将来参考,是的,SQLite3 是用 -fPIC 编译的)
狙击手
完成所有库后,我开始编译 sniper。切换到主目录,然后输入make
.一切看起来都很好,就像在我的家用机器上一样。它会遍历所有依赖项、源文件等,然后到达最后一步,即主可执行文件的链接sniper
。在这里,它突然报错,然后停止了:
[LD ] lib/sniper
/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: ~/sniper/standalone/../standalone/standalone.o: relocation R_X86_6ldrelocationssnipecompilation4_32S against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
~/sniper/standalone/../standalone/standalone.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
Makefile:34: recipe for target '~/sniper/standalone/../lib/sniper' failed
make: *** [~/sniper/standalone/../lib/sniper] Error 1
这个错误信息让我难住了。所有内容都是使用 -fPIC 编译的,因此错误肯定与某些从外部拉入的库有关。
此处执行的用于链接的命令(对于那些从未使用过狙击手的人)(它实际上使用完整路径,而不是“〜”,但我用“〜”替换了它们,因为它们包含大量个人识别信息):
g++ -L~/sniper/standalone/../lib -L~/sniper/standalone/../sift -L~/sniper/standalone/../pin_kit/extras/xed-intel64/lib -L~/sqlite/lib -L~/sniper/standalone/../pin_kit/extras/xed2-intel64/lib -o ~/sniper/standalone/../lib/sniper ~/sniper/standalone/../standalone/exceptions.o ~/sniper/standalone/../standalone/standalone.o -lcarbon_sim -lpthread -lsift -lxed -L~/sniper/standalone/../python_kit/intel64/lib -lpython2.7 -lrt -lz -lsqlite3 -lxed -O2 -g -std=c++0x
特别是,-lcarbon_sim -sift
引用之前在此过程中编译的自定义库make
,并-lxed
引用英特尔 PIN 库(用于处理器检测)。
我的想法/我尝试过的
这是一个令人惊讶的错误消息。由于某种原因,它指出standalone.o是位置相关的并且不能编译成共享对象,当全部编译步骤有-fPIC(我三重检查)。再次,我想到的唯一一件事是被拉入的库之一是在没有 -fPIC 的情况下编译的,这不太可能,我一定是忽略了一些东西。有什么方法可以让 ld 打印它引入的所有库的列表吗?例如,如果我能找出问题所在,我也许可以引入手动编译的库。
此外,我检查了我的家用计算机上的重定位,并且standalone.o 文件中存在完全相同的重定位ld 抱怨( R_X86_64_32S against .rodata.str1.1
),但那里一切正常。
我认为这可能是由于我自定义安装了 SQLite3(我通过家用计算机中的包管理器安装了它),因此我尝试通过与集群上完全相同的过程在我的家用计算机上安装一个副本。一切仍然有效,我通过 ldd 确认它实际上链接到我的副本(而不是系统版本)。
我还比较了两台机器之间的gcc、g++和ld版本,它们是匹配的。
此外,我注意到一件奇怪的事情:该文件~/sniper/lib/pin_sim.so
(由一些拉入英特尔 PIN 库的狙击代码编译而成)是一个 64 位动态链接的 ELF 可执行文件(如预期),但在我的家用计算机上运行时ldd pin_sim.so
仅打印not a dynamic executable
它打印所有使用的共享库。我尝试pin_sim.so
从我的计算机复制到集群,ldd 也无法读取它。readelf -d pin_sim.so
在两台机器上仍然有效。
这很奇怪。我能找到的 ldd 在 readelf/objdump 不失败时失败的唯一参考是在 64 位系统下的 32 位可执行文件上调用它时。在本例中,可执行文件和系统都是 64 位,所以不是这样。
我完全不知道该怎么做。今天我花了大约 5 个小时在网上搜索类似问题的解决方案并尝试了所有方法,但没有成功。希望这里有人有一些想法吗?
编辑1:两台机器之间链接器参数的比较
正如 sblingx 所建议的,我在两台机器上运行 g++,使用-v
参数来尝试找出调用链接器 (collect2) 时的差异。我稍微清理了它们(仅相关文件名,而不是整个路径),并删除了库目录(-L):
常用参数:
--eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o lib/sniper
crti.o crtn.o
standalone/exceptions.o standalone/standalone.o
-lcarbon_sim -lpthread -lsift -lxed -lpython2.7 -lrt -lz -lsqlite3 -lxed -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc
我的家用机器特有的参数(Linux Mint 17.2,sniper 工作的地方):
--sysroot=/ --build-id -z relro
crt1.o crtbegin.o crtend.o
集群机器特有的参数(不起作用的地方):
-pie -z now
Scrt1.o crtbeginS.o crtendS.o
我对链接不是很了解,但我确实注意到我的家庭设置包括crt1.o crtbegin.o crtend.o
,而集群包括Scrt1.o crtbeginS.o crtendS.o
(注意额外的“S”)。这些文件到底有什么作用,以及S文件名中的意思是什么? (我想是“共享”或“静态”之一?)
答案1
原因
看来“-pie”参数破坏了 Sniper 编译。我尝试将其添加到我的家用计算机上,但失败并出现完全相同的错误。将其从簇线中删除,链接器成功。
正如用户 siblynx 提到的,OpenSUSE(至少是集群中的一个)强制可执行文件在链接时使用 PIE,而 Linux Mint 则不这样做。
解决方案
只需添加-fno-pie
到文件中的 $(TARGET) 链接器standalone/Makefile
调用即可覆盖 COLLECT_GCC_OPTIONS -pie
,一切似乎都能正常工作。
ldd pin_sim.so
仍然不起作用,但这完全是另一个问题。实际上,我可能会为此发布一个单独的问题。