在CentOS 8上编译SPEC2006的踩坑记录
Esper曾说过,一千个科研民工有一千种编译报错
最近从实验室拿到了SPEC软件包用于测试软件性能,本满心欢喜地以为可以用SPEC提供的预编译文件直接跑,结果install.sh
直接咣的当头一棒,说是预编译文件不支持当前环境,要自己从头编译。
然而谁也没想到的是,这只是噩梦的开始。
首先来到tools/src
目录下,使用./buildtools
命令开始编译工具链。
-
gnu make
的问题。gnu-make
一上场就给我们出了一个难题:glob/libglob.a(glob.o): In function `glob_in_dir': /opt/make-3.81/glob/glob.c:1361: undefined reference to `__alloca' /opt/make-3.81/glob/glob.c:1336: undefined reference to `__alloca' /opt/make-3.81/glob/glob.c:1277: undefined reference to `__alloca' /opt/make-3.81/glob/glob.c:1250: undefined reference to `__alloca' glob/libglob.a(glob.o): In function `glob': /opt/make-3.81/glob/glob.c:575: undefined reference to `__alloca' glob/libglob.a(glob.o):/opt/make-3.81/glob/glob.c:726: more undefined references to `__alloca' follow collect2: error: ld returned 1 exit status Makefile:410: recipe for target 'make' failed make[2]: *** [make] Error 1 make[2]: Leaving directory '/opt/make-3.81' Makefile:603: recipe for target 'all-recursive' failed make[1]: *** [all-recursive] Error 1 make[1]: Leaving directory '/opt/make-3.81' Makefile:326: recipe for target 'all' failed make: *** [all] Error 2
通过查阅资料 [提出问题] [解决问题] 发现,该问题的解决者也没能对问题的产生原因给出明确的解释,好在留下了有效的解决方案: 将
make-3.81/glob/glob.c
中,第54行的# if _GNU_GLOB_INTERFACE_VERSION == GLOB_INTERFACE_VERSION
改成
# if _GNU_GLOB_INTERFACE_VERSION >= GLOB_INTERFACE_VERSION
然后重新编译解决问题。
specmd5sum
的问题。gcc -DHAVE_CONFIG_H -I/home/gem5/cpu2006/tools/output/include -I. -Ilib -c -o md5sum.o md5sum.c In file included from md5sum.c:38:0: lib/getline.h:31:1: error: conflicting types for 'getline' getline PARAMS ((char **_lineptr, size_t *_n, FILE *_stream)); ^ In file included from md5sum.c:26:0: /usr/include/stdio.h:678:20: note: previous declaration of 'getline' was here extern _IO_ssize_t getline (char **__restrict __lineptr, ^ In file included from md5sum.c:38:0: lib/getline.h:34:1: error: conflicting types for 'getdelim' getdelim PARAMS ((char **_lineptr, size_t *_n, int _delimiter, FILE *_stream)); ^ In file included from md5sum.c:26:0: /usr/include/stdio.h:668:20: note: previous declaration of 'getdelim' was here extern _IO_ssize_t getdelim (char **__restrict __lineptr, ^ make: *** [md5sum.o] Error 1 + testordie 'error building specmd5sum' + test 2 -ne 0 + echo '!!! error building specmd5sum' !!! error building specmd5sum + kill -TERM 1299 + exit 1 !!!!! buildtools killed
这次的问题出在getline
函数上,该函数属于gcc
的插件函数,也就是说既不是ANSI C标准,也不是POSIX标准,而是因为经常被使用而作为第三方的gnu扩展加入了gcc中。由于GLIBC 1中没有实现该方法,所以旧版本的程序需要自己实现该方法;而总从GLIBC2开始,该方法就写入了stdio.h
中,再用老方法编译就会产生重定义的错误了。
解决该问题的方法之一是对gcc使用-stc=c99
(其它C标准,比如C89等等也可以)让它不定义getline
函数,而是使用自带的定义。但是这样引入的问题是,实际上SPEC其他组件是采用了gnu扩展标准的,而buildtools
脚本又不支持对不同项目给出不同的命令行选项,反而又会导致其他项目编译失败。这里采用第二种方法,不使用自带的getline
,而是使用gcc提供的getline
函数。具体操作就是注释掉自带的getline.h
头文件,参见该博客。
- Perl的问题。
Perl这里一共有两个问题。第一个是asm/page.h
头文件缺失的问题,可以直接注释掉。或许也可以使用-I/usr/include/x86_64-linux-gnu
参数将该文件的新位置提供给gcc,但是我没有进行测试。
第二个是动态链接的问题。如果没有正确配置动态链接库libdl
的话,Perl就无法提供动态加载模块的功能,导致在后续的Perl模块编译与测试阶段中出现类似的提示:
t/100generic-bzip2.....Can't load module Compress::Raw::Bzip2, dynamic loading not available in this perl.
(You may need to build a new perl executable which either supports
dynamic loading or has the Compress::Raw::Bzip2 module statically linked into it.)
at /home/esper/cpu2006/tools/output/lib/perl5/site_perl/5.8.8/IO/Compress/Adapter/Bzip2.pm line 10
Compilation failed in require at /home/esper/cpu2006/tools/output/lib/perl5/site_perl/5.8.8/IO/Compress/Adapter/Bzip2.pm line 10.
BEGIN failed--compilation aborted at /home/esper/cpu2006/tools/output/lib/perl5/site_perl/5.8.8/IO/Compress/Adapter/Bzip2.pm line 10.
Compilation failed in require at /home/esper/cpu2006/tools/output/lib/perl5/site_perl/5.8.8/IO/Compress/Bzip2.pm line 11.
BEGIN failed--compilation aborted at /home/esper/cpu2006/tools/output/lib/perl5/site_perl/5.8.8/IO/Compress/Bzip2.pm line 11.
Compilation failed in require at t/100generic-bzip2.t line 12.
BEGIN failed--compilation aborted at t/100generic-bzip2.t line 12.
这里第二行给出了报错的原因:编译的Perl没有动态加载的功能,换句话说,就是用于动态加载代码的libdl
库没有正确配置,导致动态加载一块的代码完全没有编译。回去翻阅Configure
的输出,发现早在配置阶段就出现问题了:
dlopen() NOT found.
Do you wish to use dynamic loading? [n]
由于完全没有给出任何有用的信息,加上咕果上也查不到什么有效的解决方案,在此陷入了困境。
然而,天无绝人之路,后来在解决另一个报错,数学库libm
找不到的时候,意外地有了新的进展。可能是由于Configure
脚本过老,无法识别到gcc数学库参数,额外附加库这项配置变成了none
:
Checking for optional libraries...
What libraries to use? [none]
由于实际上是使用了数学库的,导致在后续编译中会出现找不到符号的问题:
../libperl.a(pp.o): In function `Perl_pp_pow':
pp.c:(.text+0x27b6): undefined reference to `pow'
../libperl.a(pp.o): In function `Perl_pp_modulo':
pp.c:(.text+0x366d): undefined reference to `fmod'
../libperl.a(pp.o): In function `Perl_pp_atan2':
pp.c:(.text+0x824d): undefined reference to `atan2'
../libperl.a(pp.o): In function `Perl_pp_sin':
pp.c:(.text+0x8341): undefined reference to `sin'
../libperl.a(pp.o): In function `Perl_pp_cos':
pp.c:(.text+0x8481): undefined reference to `cos'
../libperl.a(pp.o): In function `Perl_pp_exp':
pp.c:(.text+0x87a1): undefined reference to `exp'
../libperl.a(pp.o): In function `Perl_pp_log':
pp.c:(.text+0x88e8): undefined reference to `log'
../libperl.a(pp.o): In function `Perl_pp_sqrt':
pp.c:(.text+0x8b9f): undefined reference to `sqrt'
../libperl.a(pp.o): In function `Perl_pp_crypt':
pp.c:(.text+0xabee): undefined reference to `crypt'
collect2: error: ld returned 1 exit status
看到前面提到的一篇博客中,通过修改buildtools
的方法解决了该问题,并且我也进行了验证,确实有效。然而总觉得不够清真,为了解决一个系统上的问题修改了全局配置。后来了解到Perl的Configure
可以通过hintfile
为特定平台提供默认参数,从而解决了该问题。hintfile
将会被用类似source
的方式将变量引入当前脚本中,可以作为Configure
选项的默认值。由于没有找到详细的文档,hintfile
的变量名要自己猜,但是可以根据Configure
中用于用户交互的信息进行变量名的猜测。在Linux系统中,Configure
大概率会选择hints/linux.sh
作为hintfile
进行加载。
后来发现手动添加库依赖的变量名为libs
,把-lm
加入该变量值即可解决问题。
而后在阅读最新版Perl的hints/linux.sh
(参见 https://github.com/Perl/perl5/blob/blead/hints/linux.sh )的时候发现,针对g++和dlopen
有一段硬编码的配置:
# If using g++, the Configure scan for dlopen() and (especially)
# dlerror() might fail, easier just to forcibly hint them in.
case "$cc" in
*g++*)
d_dlopen='define'
d_dlerror='define'
;;
esac
其中提到g++和dlopen
具有一定的不兼容性,Configure
在这种条件下可能识别不出来libdl
,抱着死马当活马医的心态,把这段配置复制到SPEC2006中Perl的hintfile
中,发现没有任何变化,还是报出相同的错误。(显然不会有任何变化,默认使用的是cc
/gcc
命令,不会显式地拿g++
来编译C代码的)
然后意识到问题之后,直接把里面的两行关键代码留下来,再次编译,报错发生了变化。
首先是通过hintfile
的内置变量赋值,强行绕过了Configure
的dlopen
检测。脚本给出了如下的警告信息,意味着虽然没有检测到libdl
,但是程序忠实地按照变量配置,开启了含有libdl
库的代码编译:
dlopen() NOT found.
*** WHOA THERE!!! ***
The recommended value for $d_dlopen on this machine was "define"!
Keep the recommended value? [y]
Do you wish to use dynamic loading? [y]
然后报出了新的错误:
Making DynaLoader (static)
Processing hints file hints/linux.pl
Writing Makefile for DynaLoader
make[1]: Entering directory `/home/esper/cpu2006/tools/src/perl-5.8.8/ext/DynaLoader'
make[1]: Leaving directory `/home/esper/cpu2006/tools/src/perl-5.8.8/ext/DynaLoader'
make[1]: Entering directory `/home/esper/cpu2006/tools/src/perl-5.8.8/ext/DynaLoader'
../../miniperl "-I../../lib" "-I../../lib" DynaLoader_pm.PL DynaLoader.pm
../../miniperl "-I../../lib" "-I../../lib" XSLoader_pm.PL XSLoader.pm
cp XSLoader.pm ../../lib/XSLoader.pm
cp DynaLoader.pm ../../lib/DynaLoader.pm
AutoSplitting ../../lib/DynaLoader.pm (../../lib/auto/DynaLoader)
rm -f DynaLoader.xs
cp dl_dlopen.xs DynaLoader.xs
../../miniperl "-I../../lib" "-I../../lib" ../../lib/ExtUtils/xsubpp -noprototypes -typemap ../../lib/ExtUtils/typemap DynaLoader.xs > DynaLoader.xsc && mv DyaLoader.xsc DynaLoader.c
cc -c -I/home/esper/cpu2006/tools/output/include -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFSET_BITS=64 -O2 -DVERSION=\"1.05\" -DXS_VERSION=\"1.05\" "-I../.." -DPERL_CORE -DLIBC="" DynaLoader.c
rm -rf ../../lib/auto/DynaLoader/DynaLoader.a
/usr/bin/ar cr ../../lib/auto/DynaLoader/DynaLoader.a DynaLoader.o && : ../../lib/auto/DynaLoader/DynaLoader.a
chmod 755 ../../lib/auto/DynaLoader/DynaLoader.a
make[1]: Leaving directory `/home/esper/cpu2006/tools/src/perl-5.8.8/ext/DynaLoader'
cc -o perl -L/home/esper/cpu2006/tools/output/lib -Wl,-E perlmain.o lib/auto/DynaLoader/DynaLoader.a libperl.a `cat ext.libs` -lm -lcrypt -lutil -lc
lib/auto/DynaLoader/DynaLoader.a(DynaLoader.o): In function `XS_DynaLoader_dl_find_symbol':
DynaLoader.c:(.text+0x3b3): undefined reference to `dlsym'
DynaLoader.c:(.text+0x441): undefined reference to `dlerror'
lib/auto/DynaLoader/DynaLoader.a(DynaLoader.o): In function `XS_DynaLoader_dl_unload_file':
DynaLoader.c:(.text+0x4ea): undefined reference to `dlclose'
DynaLoader.c:(.text+0x559): undefined reference to `dlclose'
DynaLoader.c:(.text+0x566): undefined reference to `dlerror'
lib/auto/DynaLoader/DynaLoader.a(DynaLoader.o): In function `XS_DynaLoader_dl_load_file':
DynaLoader.c:(.text+0x645): undefined reference to `dlopen'
DynaLoader.c:(.text+0x6c9): undefined reference to `dlerror'
collect2: error: ld returned 1 exit status
make: *** [perl] Error 1
+ testordie 'error building Perl'
+ test 2 -ne 0
+ echo '!!! error building Perl'
!!! error building Perl
+ kill -TERM 550221
+ exit 1
!!!!! buildtools killed
我的眼神瞬间犀利了起来.jpg
undefined reference to ...
,这不是最经典的忘记加-lXXX
链接参数的结果吗?
除错终了后我神速开hintfile
。目的唯一libs
!!!!!我爆速修改链接选项。warning!
此处省略100余字
修改文件过后,果然编译成功!删去上文根据设想添加的两行d_XXX
强制配置后,编译仍然通过,这证实了我的设想。
然而,Perl的测试中仍然有几个是处于失败状态的,大约1%,十来个左右。由于测试范围往往大于使用需求,对于这几个没通过的测试我就没理它了,直接yes进行后续编译操作。
总结就是,Perl编译动态加载模块失败的原因与数学库一样,都是因为识别不到系统配置,默认配置又不提供有效值造成的。在perl-5.8.8/hints/linux.sh
末尾加上如下两行解决上述编译问题:
libs='-lm -lcrypt -lutil -lc -ldl'
libpth='/usr/local/lib64 /lib64 /usr/lib64'
- 自此之后编译上就应该没有本质上的问题了。如果是直接从
.tar.gz
文件解压的话,复制出来的src
文件夹及下级文件权限都是444
,即只有读权限,而后续某些模块的测试涉及到对测试文件的读写操作,会产生Permission Denied
一类的报错信息。如果对自己的操作自信,不会损坏文件的话,可以使用偷懒的方法,直接给所有文件/文件夹写权限:sudo chmod u+w -R .
在
tools/src
目录下执行该命令即可。
中间还有个小插曲是,一开始以为是编译器版本的问题,整了gcc 4和5的docker来编译看能不能成功。结果不但没成功,还报出了新的You haven't done a "make depend" yet!
错误,后来发现原因是gcc镜像的FROM
用的是Debian系的,/bin/sh
默认指向了dash
而不是bash
。用ln -snf /bin/bash /bin/sh
覆盖修改这个链接即可。更多资料可参见 https://sjp38.github.io/post/spec_cpu2006_install/ 。
ps. 如果在后续测试阶段发现某些测试怎么跑都跑不对,可以检查一下是不是拿到的SPEC有问题。我这里用到的SPEC是被实验室重新打包过的,里面好多原始数据文件被改过了,debug了好久才发现:(