升级gcc

CentOS 7.9默认安装的gcc版本为4.8.5,编译glibc 2.28(Node.js从18开始,要求glibc最低为2.28)要求:

  • gcc不能低于4.9,推荐gcc版本:8.5
  • make版本不能低于4.0,也不能高于4.3。make(>=4.4)的jobserver与glibc(<=2.31)不兼容,推荐make版本:4.3

升级gcc有两种方法:

安装devtoolset-8

[root@ecs ~]# yum install centos-release-scl -y
[root@ecs ~]# sed -i 's%mirrorlist=http://mirrorlist.centos.org?arch=$basearch&release=7&repo=sclo-sclo%baseurl=http://mirrors.aliyun.com/centos/7/sclo/$basearch/sclo/%g' /etc/yum.repos.d/CentOS-SCLo-scl.repo
[root@ecs ~]# sed -i 's%mirrorlist=http://mirrorlist.centos.org?arch=$basearch&release=7&repo=sclo-rh%baseurl=http://mirrors.aliyun.com/centos/7/sclo/$basearch/rh/%g' /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo
[root@ecs ~]# yum repolist && yum clean all && yum makecache
[root@ecs ~]# yum install devtoolset-8 -y
[root@ecs ~]# echo "source /opt/rh/devtoolset-8/enable" >> /etc/profile
[root@ecs ~]# source /etc/profile
[root@ecs ~]# gcc -v 2>&1 | tail -n 1
gcc version 8.3.1 20190311 (Red Hat 8.3.1-3) (GCC)
[root@ecs ~]# make -v | head -1
GNU Make 4.2.1

devtoolset-8安装简单且速度快,但不会升级libstdc++,可以下载一个编译好的高版本libstdc++覆盖CentOS 7.9的默认版本。

[root@ecs ~]# mv -f /usr/local/src/libstdc++.so.6.0.25 /usr/lib64/libstdc++.so.6.0.25
[root@ecs ~]# ln -sfv /usr/lib64/libstdc++.so.6.0.25 /usr/lib64/libstdc++.so.6

警告:此方法只适用于升级libstdc++,切勿使用编译好的libm-2.17.so或libc-2.28.so来升级libm和libc,一旦操作,系统必崩溃!

编译安装gcc

gcc下载地址:https://ftp.gnu.org/pub/gnu/gcc/

阿里云镜像:https://mirrors.aliyun.com/gnu/gcc/

gcc源码安装会升级libstdc++,但是安装时间相当长(可能1小时以上)。

gcc 8.5.0

[root@ecs ~]# yum install -y bzip2 m4 gcc gcc-c++

[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -jxvf gmp-6.3.0.tar.bz2
[root@ecs src]# cd /usr/local/src/gmp-6.3.0
[root@ecs gmp-6.3.0]# ./configure
[root@ecs gmp-6.3.0]# make && make install

[root@ecs gmp-6.3.0]# cd /usr/local/src
[root@ecs src]# tar -jxvf mpfr-4.2.1.tar.bz2
[root@ecs src]# cd /usr/local/src/mpfr-4.2.1
[root@ecs mpfr-4.2.1]# ./configure
[root@ecs mpfr-4.2.1]# make && make install
[root@ecs mpfr-4.2.1]# cp /usr/local/lib/libmpfr.so.6 /usr/lib64

[root@ecs mpfr-4.2.1]# cd /usr/local/src
[root@ecs src]# tar -zxvf mpc-1.3.1.tar.gz
[root@ecs src]# cd /usr/local/src/mpc-1.3.1
[root@ecs mpc-1.3.1]# ./configure
[root@ecs mpc-1.3.1]# make && make install

[root@ecs mpc-1.3.1]# cd /usr/local/src
[root@ecs src]# tar -zxvf gcc-8.5.0.tar.gz
[root@ecs src]# cd /usr/local/src/gcc-8.5.0
[root@ecs gcc-8.5.0]# mkdir build && cd build
[root@ecs build]# ../configure --enable-languages=c,c++ --disable-multilib
[root@ecs build]# make -j$(nproc) && make install
[root@ecs build]# cp /usr/local/lib64/libstdc++.so.6.0.25 /usr/lib64
[root@ecs build]# ln -sfv /usr/lib64/libstdc++.so.6.0.25 /usr/lib64/libstdc++.so.6
[root@ecs build]# gcc --version | head -1
gcc (GCC) 8.5.0

gcc 9.5.0

[root@ecs ~]# yum install -y bzip2 m4 gcc gcc-c++

[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -jxvf gmp-6.3.0.tar.bz2
[root@ecs src]# cd /usr/local/src/gmp-6.3.0
[root@ecs gmp-6.3.0]# ./configure
[root@ecs gmp-6.3.0]# make && make install

[root@ecs gmp-6.3.0]# cd /usr/local/src
[root@ecs src]# tar -jxvf mpfr-4.2.2.tar.bz2
[root@ecs src]# cd /usr/local/src/mpfr-4.2.2
[root@ecs mpfr-4.2.2]# ./configure
[root@ecs mpfr-4.2.2]# make && make install
[root@ecs mpfr-4.2.2]# cp /usr/local/lib/libmpfr.so.6 /usr/lib64

[root@ecs mpfr-4.2.2]# cd /usr/local/src
[root@ecs src]# tar -xvf mpc-1.4.1.tar.xz
[root@ecs src]# cd /usr/local/src/mpc-1.4.1
[root@ecs mpc-1.4.1]# ./configure
[root@ecs mpc-1.4.1]# make && make install

[root@ecs mpc-1.4.1]# cd /usr/local/src
[root@ecs src]# tar -zxvf gcc-9.5.0.tar.gz
[root@ecs src]# cd /usr/local/src/gcc-9.5.0
[root@ecs gcc-9.5.0]# mkdir build && cd build
[root@ecs build]# ../configure --enable-languages=c,c++ --disable-multilib
[root@ecs build]# make -j$(nproc) && make install
[root@ecs build]# cp /usr/local/lib64/libstdc++.so.6.0.28 /usr/lib64
[root@ecs build]# ln -sfv /usr/lib64/libstdc++.so.6.0.28 /usr/lib64/libstdc++.so.6
[root@ecs build]# gcc --version | head -1
gcc (GCC) 9.5.0
gcc版本号libstdc++版本号
8.5.025
9.5.028
10.5.028
11.1.029
12.1.030
13.1.031
14.1.0 ~ 14.4.033
15.1.034
16.1.035

gcc 15.1.0要求gcc环境必须支持C++14,因此必须升级gcc到5.0或更高版本(推荐14.1.0),gcc 4.8开始部分支持C++14(-std=c++1y),4.9支持C++14的核心特性,5.0开始完全对齐C++14标准。

gcc 15.1.0默认开启了-Werror,即警告视为报错。比如原本只是警告的discarded-qualifiers(丢弃了const限定符),开启了-Werror之后被认为是报错,从而中断编译。必须在configure时加上--disable-werror来忽略这些报错。

升级make

make下载地址:https://ftp.gnu.org/pub/gnu/make/

安装devtoolset-8会升级make到4.2.1,无需单独升级make。

[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -xzvf make-4.3.tar.gz && cd make-4.3
[root@ecs make-4.3]# ./configure  --prefix=/usr/local/make
[root@ecs make-4.3]# make && make install
[root@ecs make-4.3]# mv /usr/bin/make /usr/bin/make.3.82
[root@ecs make-4.3]# ln -sv /usr/local/make/bin/make /usr/bin/make
[root@ecs make-4.3]# make -v | head -1
GNU Make 4.3

升级glibc

glibc下载地址:https://ftp.gnu.org/pub/gnu/glibc

升级glibc会生成高版本的libm和libc。

glibc 2.28

[root@ecs ~]# yum install -y gcc bison
[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -zxvf glibc-2.28.tar.gz && cd glibc-2.28
[root@ecs glibc-2.28]# mkdir build && cd build
[root@ecs build]# ../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
[root@ecs build]# make -j$(nproc) && make install
[root@ecs build]# ll /lib64/libc.so.6
lrwxrwxrwx. 1 root root 12 Jul  9 20:33 /lib64/libc.so.6 -> libc-2.28.so
[root@ecs build]# strings /lib64/libc.so.6 | grep ^GLIBC | sort -t'.' -k 2,2 -n -r | head -1
GLIBC_2.28

注:glibc 2.29开始要求python>=3.4,使用yum install python3 -y安装python3;

常见报错

  • make install时报错:/usr/bin/ld: cannot find -lnss_test2

    报错原因是scripts/test-installation.pl调用了gcc -lnss_test2,而make过程中并没有生成libnss_test2.so,从而导致了报错。

    devtoolset升级gcc后安装glibc,必定出现这个报错,而编译安装gcc后安装glibc则不一定会出现这个报错。究其原因是因为devtoolset会自动安装perl,而编译安装gcc则不会,在没有安装perl的情况下是无法执行该脚本的,因此也就不会报错了。

    解决方法:test-installation.pl中gcc不要调用-lnss_test2,在scripts/test-installation.pl脚本128行前面插入\t&& $name ne "nss_test2"

  • 报错:/lib/../lib64/libnss_nis.so: undefined reference to `_nsl_default_nss@GLIBC_PRIVATE'

    libnss_nis.so依赖_nsl_default_nss@GLIBC_PRIVATElibnss_nis.so自身并没有定义_nsl_default_nss@GLIBC_PRIVATE,而是调用libnsl.so的。glibc2.28默认移除了nsl的支持,因此生成的libnsl.so并不包含_nsl_default_nss@GLIBC_PRIVATE。configure时加上--enable-obsolete-nsl后,生成的libnsl.so即可包含_nsl_default_nss@GLIBC_PRIVATE

    查看定义:

    [root@ecs ~]# readelf -Ws /lib64/libnss_nis.so | grep _nsl_default_nss
        16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _nsl_default_nss@GLIBC_PRIVATE (6)
       155: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _nsl_default_nss@@GLIBC_PRIVATE
    [root@ecs ~]# nm -D /lib64/libnss_nis.so | grep _nsl_default_nss
                     U _nsl_default_nss
    [root@ecs ~]# readelf -Ws /lib64/libnsl.so | grep _nsl_default_nss
       210: 000000000000ef80    81 FUNC    GLOBAL DEFAULT   12 _nsl_default_nss@@GLIBC_PRIVATE
       371: 000000000000ef80    81 FUNC    GLOBAL DEFAULT   12 _nsl_default_nss

    UND表示未定义函数,只是调用。

    解决方法(二选一):

    • 让glibc支持nsl,configure时添加--enable-obsolete-nsl
    • test-installation.pl中gcc不要调用-lnss_nis,在scripts/test-installation.pl脚本128行前面插入\t&& $name ne "nss_nis"

    如果出现以上报错,则使用以下方法安装glibc:

    [root@ecs ~]# yum install -y gcc bison
    [root@ecs ~]# cd /usr/local/src
    [root@ecs src]# tar -zxvf glibc-2.28.tar.gz && cd glibc-2.28
    [root@ecs glibc-2.28]# mkdir build && cd build
    [root@ecs build]# ../configure --prefix=/usr --disable-profile --enable-add-ons \
    --with-headers=/usr/include --with-binutils=/usr/bin \
    --enable-obsolete-nsl
    [root@ecs build]# sed -i '128i\\t&& $name ne "nss_test2"' ../scripts/test-installation.pl
    [root@ecs build]# sed -i '128i\\t&& $name ne "nss_nis"' ../scripts/test-installation.pl
    [root@ecs build]# make -j$(nproc) && make install
    [root@ecs build]# strings /lib64/libc.so.6 | grep ^GLIBC | sort -t'.' -k 2,2 -n -r | head -1
    GLIBC_2.28

    监控安装过程

    [root@ecs ~]# yum install inotify-tools -y
    [root@ecs ~]# inotifywait -m -r -e create --format '%T %w%f %e' --timefmt '%Y-%m-%d %H:%M:%S' /usr/local/src/glibc-2.28 | grep "nss_test"
    • -m:monitor,持续监控;
    • -r:recursive,递归监控;
    • -e create:event,只监控创建事件;
    • --format '%T %w%f %e'

      • %T:timestamp,时间戳;
      • %w:watched path,被监控目录路径;
      • %f:filename,发生变化文件文件名;
      • %e:event,事件名

注意事项

glibc版本注意事项
2.28-2.31make>=4.0且make<=4.3
2.32-2.33make>=4.0
2.34-2.36必须先使用sln统一libc.so.6和ld-linux-x86-64.so.2的版本后再make install
2.37-2.41binutils>=2.36且configure时加上:CC="gcc -B/usr/local/bin"
2.42gcc>=12.1.0
2.43openat2要求Linux内核>=5.6,而CentOS 7.9的默认内核是3.10.0,必须先升级系统内核

同步libc和ld

从glibc 2.34开始,libc.so.6在运行时需要调用ld-linux-x86-64.so.2的一个内部私有符号__tunable_get_val。在编译安装glibc 2.34的过程中,libc.so.6升级到2.34后,ld-linux-x86-64.so.2还是旧版的,而旧版不存在私有符号__tunable_get_val,libc.so.6找不到这个私有符号就会报错:gcc: relocation error: /lib64/libc.so.6: symbol __tunable_get_val, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2 with link time reference

几乎所有Linux命令(包括 ls, cp, ln, sudo, ssh 等)都依赖这两个库,因此一旦出现这个报错,基本就要重装系统了。

为了避免新版libc.so.6调用旧版ld-linux-x86-64.so.2的情况,使用sln(不需要ld加载动态链接库)先强制将libc.so.6和ld-linux-x86-64.so.2都链接到glibc 2.34新编译的版本(在build目录内),再执行make install。

glibc 2.34

[root@ecs ~]# yum install -y gcc bison python3
[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -zxvf glibc-2.34.tar.gz && cd glibc-2.34
[root@ecs glibc-2.34]# mkdir build && cd build
[root@ecs build]# ../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
[root@ecs build]# make -j$(nproc)
[root@ecs build]# sln /usr/local/src/glibc-2.34/build/libc.so.6 /lib64/libc.so.6
[root@ecs build]# sln /usr/local/src/glibc-2.34/build/dlfcn/libdl.so.2 /lib64/libdl.so.2
[root@ecs build]# sln /usr/local/src/glibc-2.34/build/nptl/libpthread.so.0 /lib64/libpthread.so.0
[root@ecs build]# sln /usr/local/src/glibc-2.34/build/elf/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2
[root@ecs build]# make install
[root@ecs build]# strings /lib64/libc.so.6 | grep ^GLIBC | sort -t'.' -k 2,2 -n -r | head -1
GLIBC_2.34

sln不依赖libc.so.6,因此设置LD_PRELOAD=/lib64/libc-2.17.so 没有任何效果。

[root@ecs ~]# file /sbin/sln
/sbin/sln: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, not stripped
[root@ecs ~]# ldd /sbin/sln
        not a dynamic executable

升级binutils

glibc从2.37开始使用了AVX-512的vpcmpneqb指令,binutils(包含as、ld等工具)从2.36开始才支持这个指令。使用低版本的binutils编译glibc 2.37会报错:Error: no such instruction: `vpcmpneqb (64 * 4)(%rdi),%zmm17,%k1'

CentOS 7.9默认的binutils是2.27,必须升级binutils(推荐2.43)才能支持vpcmpneqb。升级方法也适用于最新版2.46.1。

[root@ecs ~]# yum install -y texinfo bison
[root@ecs ~]# cd /usr/local/src
[root@ecs src]# wget https://ftp.gnu.org/gnu/binutils/binutils-2.43.tar.xz
[root@ecs src]# tar -xvf binutils-2.43.tar.xz
[root@ecs src]# cd binutils-2.43/
[root@ecs binutils-2.40]# ./configure
[root@ecs binutils-2.40]# make && make install

测试升级是否成功:

[root@ecs ~]# echo "vpcmpneqb (64 * 4)(%rdi),%zmm17,%k1" | as -o /dev/null - 2>&1

查看as版本:

[root@ecs ~]# as --version | head -1
GNU assembler (GNU Binutils) 2.40

查看gcc调用的as版本

[root@ecs ~]# $(gcc -print-prog-name=as) --version | head -1
GNU assembler (GNU Binutils) 2.40

查看PATH优先级

[root@ecs ~]# echo $PATH | tr ':' '\n' | grep -E "binutils|local"
/usr/local/sbin
/usr/local/bin

strace跟踪实际调用

[root@ecs ~]# yum install -y strace
[root@ecs ~]# strace -f -e execve make -j1 2>&1 | grep "execve.*as" | head -5

glibc 2.37

虽然升级了binutils,但是升级后的as安装路径是/usr/local/bin/as,而glibc 2.37在编译的时候会调用/usr/bin/as,此as版本还是旧版,依旧会报错:Error: no such instruction: `vpcmpneqb (64 * 4)(%rdi),%zmm17,%k1'。因此必须在configure时指定binutils升级后的安装目录:configure CC="gcc -B/usr/local/bin"

[root@ecs ~]# yum install -y gcc bison python3
[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -zxvf glibc-2.37.tar.gz && cd glibc-2.37
[root@ecs glibc-2.37]# mkdir build && cd build
[root@ecs build]# ../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin CC="gcc -B/usr/local/bin"
[root@ecs build]# make -j$(nproc)
[root@ecs build]# sln /usr/local/src/glibc-2.37/build/libc.so.6 /lib64/libc.so.6
[root@ecs build]# sln /usr/local/src/glibc-2.37/build/dlfcn/libdl.so.2 /lib64/libdl.so.2
[root@ecs build]# sln /usr/local/src/glibc-2.37/build/nptl/libpthread.so.0 /lib64/libpthread.so.0
[root@ecs build]# sln /usr/local/src/glibc-2.37/build/elf/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2
[root@ecs build]# make install

升级Linux内核

  1. 查看当前内核

    [root@ecs ~]# uname -r
    3.10.0-1160.53.1.el7.x86_64
  2. 安装kernel-ml

    下载地址:https://dl.lamp.sh/kernel/el7/

    [root@ecs ~]# rpm -ivh kernel-ml-6.9.10-1.el7.x86_64.rpm
    Preparing...                          ################################# [100%]
    Updating / installing...
    1:kernel-ml-6.9.10-1.el7           ################################# [100%]
    [root@ecs ~]# rpm -ivh kernel-ml-devel-6.9.10-1.el7.x86_64.rpm
    Preparing...                          ################################# [100%]
    Updating / installing...
    1:kernel-ml-devel-6.9.10-1.el7     ################################# [100%]
    [root@ecs ~]# rpm -e --nodeps kernel-headers
    [root@ecs ~]# rpm -ivh kernel-ml-headers-6.9.10-1.el7.x86_64.rpm
    Preparing...                          ################################# [100%]
    Updating / installing...
    1:kernel-ml-headers-6.9.10-1.el7   ################################# [100%]

    注:必须先卸载kernel-headers,否则无法安装kernel-ml-headers。卸载kernel-headers时要加上--nodeps以防止级联卸载glibc-devel等。

  3. 查看内核列表

    [root@ecs ~]# cat /boot/grub2/grub.cfg |grep ^menuentry
    menuentry 'CentOS Linux (6.9.10-1.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-6.9.10-1.el7.x86_64-advanced-14b582ff-4423-4795-9cf7-6ab774787c65' {
    menuentry 'CentOS Linux (3.10.0-1160.119.1.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-1160.119.1.el7.x86_64-advanced-14b582ff-4423-4795-9cf7-6ab774787c65' {
    menuentry 'CentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-1160.el7.x86_64-advanced-14b582ff-4423-4795-9cf7-6ab774787c65' {
    menuentry 'CentOS Linux (0-rescue-10ba6baecb054ccbad49bfb149680c75) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-0-rescue-10ba6baecb054ccbad49bfb149680c75-advanced-14b582ff-4423-4795-9cf7-6ab774787c65' {
  4. 设置启动内核

    [root@ecs ~]# grub2-set-default 0

    或者:

    [root@ecs ~]# grub2-set-default 'CentOS Linux (6.9.10-1.el7.x86_64) 7 (Core)'
  5. 生成grub2配置文件

    [root@ecs ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
    Generating grub configuration file ...
    Found linux image: /boot/vmlinuz-6.9.10-1.el7.x86_64
    Found initrd image: /boot/initramfs-6.9.10-1.el7.x86_64.img
    Found linux image: /boot/vmlinuz-3.10.0-1160.119.1.el7.x86_64
    Found initrd image: /boot/initramfs-3.10.0-1160.119.1.el7.x86_64.img
    Found linux image: /boot/vmlinuz-3.10.0-1160.el7.x86_64
    Found initrd image: /boot/initramfs-3.10.0-1160.el7.x86_64.img
    Found linux image: /boot/vmlinuz-0-rescue-10ba6baecb054ccbad49bfb149680c75
    Found initrd image: /boot/initramfs-0-rescue-10ba6baecb054ccbad49bfb149680c75.img
    done
  6. 重启

    [root@ecs ~]# reboot
  7. 查看默认启动内核

    [root@ecs ~]# grubby --default-kernel
    /boot/vmlinuz-6.9.10-1.el7.x86_64

标签: none

添加新评论