由于用于构建 GCC 的 CPU 的体系结构,这是 g++ 的“非法指令错误”吗?
Posted
技术标签:
【中文标题】由于用于构建 GCC 的 CPU 的体系结构,这是 g++ 的“非法指令错误”吗?【英文标题】:Is this g++ 'illegal instruction error' due to the architecture of the CPU that was used to build GCC? 【发布时间】:2018-11-30 19:33:38 【问题描述】:我们有一个基于 centos 的 docker 镜像,它使用 gcc 5.4 构建大型 C++ 代码库。 docker 镜像从源代码构建和安装 gcc。由于我们的私有 Docker 注册表中的一些数据丢失,我们不得不重建/推送这个 Docker 镜像到我们的注册表,并且开始发现使用这个 Docker 镜像的本地构建存在问题。
我们看到的错误是:
/usr/include/c++/5.4.0/limits:1601:7: internal compiler error: Illegal instruction
max() _GLIBCXX_USE_NOEXCEPT return __FLT_MAX__;
^
0xa4f0cf crash_signal
../../gcc-5.4.0/gcc/toplev.c:383
我的理论是这个错误是由于运行构建的底层 CPU 的架构造成的,因为我们是从源代码构建 GCC。
以前,我们有一个基于 Xeon E5 v3 CPU(Haswell 架构)的 CI 基础架构。这个 docker 镜像的构建最初是在其中一台 CI 机器上完成的,因此在本地 Haswell 开发盒上运行良好。我们的 CI 基础架构已迁移到使用 Xeon Platinum CPU(Skylake 架构)。当我重建图像时,我在我们的一个新 Skylake 盒子上进行了重建。
由于我有一个较新的开发盒,我有一个基于 Broadwell 的 CPU,无法在本地重现该问题。我们的 CI 构建运行良好。本地出现此错误的用户有一个 Haswell CPU。
我的理论合理吗?我已经要求用户在他们的 CPU 上本地构建我们的 docker 映像并测试结果,但是有没有更通用的解决方法?
我遇到了this answer,它把我指向this documentation,其中指出我可以通过-march=***
自己指定处理器架构。我由此产生的想法是:
-
在构建 GCC 时设置
-march=haswell
以防止启用较新的指令集
在为 Haswell 上不可用但 Broadwell/Skylake 上存在的指令集扩展构建 GCC 时设置 -mno-***
。
作为参考,lscpu
的输出具有 Haswell 框上不存在的这些 Broadwell 标志(与 -mno-***
标志相关联):
3dnowprefetch
hle
rtm
rdseed
adx
smap
arch_capabilities
如果这些想法中的任何一个解决了问题,是否值得测试?我希望得到一些外部输入,因为这个 docker 构建的开发循环非常长,老实说,我不知道这些 -m
标志会解决问题。
也供参考,下面是我们构建 gcc 的方式:
# build/install gcc
RUN tar xvf /tmp/archive/gcc-5.4.0.tar.gz && \
mkdir gcc-build && \
pushd gcc-build && \
../gcc-5.4.0/configure --prefix=/usr --enable-languages=c,c++,fortran --disable-multilib --with-gmp=/usr --with-mpfr=/usr --with-mpc=/usr && \
make -j32 && \
popd && \
yum remove -y gcc gcc-c++ gcc-gfortran && \
pushd gcc-build && \
make install && \
popd && \
rm -rf gcc-build gcc-5.4.0
【问题讨论】:
@MatthieuBrucher:这是预期的方向。 “当我重建映像时,我在我们的一个新 Skylake 盒子上进行了重建。在本地收到此错误的用户使用的是 Haswell CPU。” 答案就在问题中。最可能的原因是 CPU 架构不匹配,首先想到的是重建到原生目标。 GCC 默认不执行本地构建,正因为如此,你的 Dockerfile 不会改变这一点。这是另一回事。尝试获取 coredump(使用 -dH 运行 gcc 可能会有所帮助)并查看错误指令。另请注意,根本不建议以这种方式替换系统编译器,这实际上可能是问题的根本原因。 【参考方案1】:如 wikipedia (https://en.wikipedia.org/wiki/List_of_Intel_CPU_microarchitectures) 所示,Skylake 在 Broadwell 之后,本身在 Haswell 之后。
因此,Skylake 上的此类构建可能无法在较旧的 CPU 上运行,因此您应始终将 -march=haswell
添加到您的默认构建中,以生成必须在 Haswell 及更高版本上运行的二进制文件。
使用 -march
为您的最小平台调整架构,因为您知道在启用其他指令集时可能会出现数值差异。
您还可以使用-mtune
指定您将优化代码的目标(这意味着在此平台上,代码应该更快)。只要march
低于mtune
,你可以混合使用。
【讨论】:
如果大部分盒子都是skylake,那么可能-march=haswell -mtune=skylake
确实,如果生产箱是 Skylake,-mtune
是有道理的。
谢谢!我不清楚这个错误是由于 gcc 的构建还是我们的源代码的构建。是否应该将其添加到 docker 构建过程或我们的构建脚本(或两者)?
我会建议两者。我想测试是在您构建的机器上完成的,但是如果二进制文件随后用于其他用途,那么您将遇到同样的问题。以上是关于由于用于构建 GCC 的 CPU 的体系结构,这是 g++ 的“非法指令错误”吗?的主要内容,如果未能解决你的问题,请参考以下文章
使用 gcc 32bit 编译,作为带有 -m64 的 64 位程序
GitLab CI/CD 管道在构建 Debian 包时找不到 GCC