机房有些2016年前的服务器,因为硬件比较老,尝试装新系统发现各种问题,无奈装回centos7 , 但是现在的很多软件都是依赖新版的glibc,centos 7的 glibc还是2.17,无法运行编译搞了一台,效率太低,所以才有这个文档。


1. 升级 GCC

# 下载
wget https://mirrors.aliyun.com/gnu/gcc/gcc-10.1.0/gcc-10.1.0.tar.gz
tar -zxvf gcc-10.1.0.tar.gz
cd gcc-10.1.0

# 安装依赖(bzip2 用于解压 .tar.bz2)
yum -y install bzip2 gcc-c++

# 自动下载 gmp/mpfr/mpc/isl
./contrib/download_prerequisites

# 编译安装
mkdir build && cd build
../configure --enable-checking=release --enable-languages=c,c++ --disable-multilib --prefix=/usr
make -j$(nproc)
make install

验证:

gcc -v

2. 升级 make

wget https://mirrors.aliyun.com/gnu/make/make-4.4.tar.gz
tar -zxvf make-4.4.tar.gz
cd make-4.4

mkdir build && cd build
../configure --prefix=/usr && make -j$(nproc) && make install

验证:

make -v

3. 升级 binutils

wget https://mirrors.aliyun.com/gnu/binutils/binutils-2.30.tar.gz
tar -zxvf binutils-2.30.tar.gz
cd binutils-2.30

./configure --prefix=/usr
make -j$(nproc) && make install

验证(注意:没有 binutils 这个可执行文件!):

ld -v            # GNU ld (GNU Binutils) 2.30
as --version     # GNU assembler (GNU Binutils) 2.30

4. 升级 bison(含 m4 依赖)

bison 依赖 GNU M4,如果系统没有需要先装 m4。

4.1 安装 m4(如已安装可跳过)

wget https://mirrors.aliyun.com/gnu/m4/m4-1.4.18.tar.gz
tar -zxvf m4-1.4.18.tar.gz
cd m4-1.4.18

./configure --prefix=/usr && make -j$(nproc) && make install

验证:

m4 --version    # GNU M4 1.4.18

4.2 安装 bison

wget https://mirrors.aliyun.com/gnu/bison/bison-3.0.1.tar.gz
tar -zxvf bison-3.0.1.tar.gz
cd bison-3.0.1

./configure --prefix=/usr && make -j$(nproc) && make install

验证:

bison --version    # GNU Bison 3.0.1

5. 升级 glibc(⚠️ 高风险操作)

5.1 前提:理解危险

glibc 是系统中最底层的库

  • /lib64/libc.so.6 — C 标准库,所有程序依赖它
  • /lib64/ld-linux-x86-64.so.2 — 动态链接器,加载程序的入口

运行中的系统上直接 make install 替换 glibc,等于飞机飞行中换引擎。
LFS 官方教程是在 chroot 环境 中安装,而不是在宿主机上直接覆盖。

5.2 查看依赖

cd glibc-2.37
cat INSTALL | grep -E "newer|later"

glibc 2.37 依赖:

  • GNU make 4.0+
  • GCC 6.2+
  • GNU binutils 2.25+
  • GNU bison 2.7+
  • GNU sed 3.02+
  • Python 3.4+
  • GNU gettext 0.10.36+

5.3 正确安装方式

方式一:安装到独立目录(--prefix=/opt/glibc

如果只是想在系统上装一个新版 glibc 作为工具链的一部分(而非替换系统 glibc),直接指定独立 prefix:

cd /srv/glibc-2.37
mkdir build && cd build

../configure --prefix=/opt/glibc               \
             --disable-profile                  \
             --enable-add-ons                   \
             --with-headers=/usr/include        \
             --with-binutils=/usr/bin

make -j$(nproc)
make install

安装后的目录结构:

/opt/glibc/
├── bin/          # ldd, locale, localedef 等工具
├── include/      # 头文件
├── lib/          # 静态库、crt*.o
├── lib64/        # libc.so.6, ld-linux-x86-64.so.2, libm, libpthread 等
├── sbin/         # ldconfig, zic 等
└── share/        # locale 数据、man

系统 /lib64 完全不受影响,新旧 glibc 各自独立。

如何使用这个独立 glibc:

直接运行已有程序(验证链接)
# 方法一:用新 glibc 的 ld.so 显式启动
/opt/glibc/lib64/ld-linux-x86-64.so.2 \
    --library-path /opt/glibc/lib64 \
    /bin/ls

# 方法二:设置 LD_LIBRARY_PATH
LD_LIBRARY_PATH=/opt/glibc/lib64 /bin/ls
编译时链接新 glibc
# 方法一:指定动态链接器和 rpath(推荐,固化进 ELF)
gcc -Wl,-rpath,/opt/glibc/lib64                     \
    -Wl,-dynamic-linker,/opt/glibc/lib64/ld-linux-x86-64.so.2 \
    -o myapp myapp.c

# 方法二:用 ld.so 包装编译(适合一次性测试)
/opt/glibc/lib64/ld-linux-x86-64.so.2 \
    --library-path /opt/glibc/lib64   \
    gcc -o myapp myapp.c

# 方法三:--sysroot(编译整个工具链时常用)
gcc --sysroot=/opt/glibc -o myapp myapp.c
# --sysroot 会在所有搜索路径前自动加 /opt/glibc 前缀:
#   头文件:   /opt/glibc/usr/include
#   库文件:   /opt/glibc/lib64 或 /opt/glibc/usr/lib64
#   动态链接器: 自动指向 /opt/glibc/lib64/ld-linux-x86-64.so.2
验证编译产物
# 看链接了哪个 libc
ldd ./myapp
# libc.so.6 => /opt/glibc/lib64/libc.so.6    ← 新 glibc

# 看用的是哪个 ld.so
readelf -l ./myapp | grep interpreter
# [Requesting program interpreter: /opt/glibc/lib64/ld-linux-x86-64.so.2]

# 不设 LD_LIBRARY_PATH 也能跑(因为 rpath 固化进去了)
./myapp
实战:用新 glibc 运行 Qwen Code

Qwen Code 是 Node.js 应用,Node 本身动态链接 glibc。让 Qwen Code 跑在新 glibc 上:

# 环境信息(宿主机)
# Qwen Code: /soft/soft/node-v26.5.1/bin/qwen
# Node.js:   /soft/soft/node-v26.5.1/bin/node
# 新 glibc:  /opt/glibc/lib64/

方法一:LD_LIBRARY_PATH(最简单,临时用)

LD_LIBRARY_PATH=/opt/glibc/lib64 qwen

# 或者显式指定 node
LD_LIBRARY_PATH=/opt/glibc/lib64 /soft/soft/node-v26.5.1/bin/node \
    /soft/soft/node-v26.5.1/lib/node_modules/@qwen-code/qwen-code/cli-entry.js

方法二:ld.so 显式启动(无需设环境变量)

/opt/glibc/lib64/ld-linux-x86-64.so.2 \
    --library-path /opt/glibc/lib64   \
    /soft/soft/node-v26.5.1/bin/node  \
    /soft/soft/node-v26.5.1/lib/node_modules/@qwen-code/qwen-code/cli-entry.js

方法三:包装脚本(固化配置)

# 创建 ~/bin/qwen-new,写入:
cat > ~/bin/qwen-new << 'EOF'
#!/bin/bash
export LD_LIBRARY_PATH=/opt/glibc/lib64:$LD_LIBRARY_PATH
exec /soft/soft/node-v26.5.1/bin/node \
    /soft/soft/node-v26.5.1/lib/node_modules/@qwen-code/qwen-code/cli-entry.js "$@"
EOF
chmod +x ~/bin/qwen-new

# 以后直接用
qwen-new

验证是否跑在新 glibc 上:

# 看 node 进程链接的 libc
LD_LIBRARY_PATH=/opt/glibc/lib64 ldd /soft/soft/node-v26.5.1/bin/node | grep libc
# libc.so.6 => /opt/glibc/lib64/libc.so.6    ← 新 glibc
方式二:chroot(LFS 官方方式)

LFS(Linux From Scratch)的核心思路:在宿主机上构建一套完整的新工具链,安装到一个独立分区或目录,然后 chroot 进去,在那个隔离环境中完成最终构建。这样新 glibc 安装时不会影响宿主机的运行。

# ============================================
# 阶段一:准备工作(在宿主机上)
# ============================================

# 假设目标分区挂载在 /mnt/lfs
export LFS=/mnt/lfs

# 创建目录结构
mkdir -pv $LFS/{bin,etc,lib,lib64,sbin,usr,var}
mkdir -pv $LFS/tools

# 关键:为 chroot 环境建立 ld.so 软链
# (此时 lib64/ 是空的,先指向宿主机的 ld.so 让程序能跑)
case $(uname -m) in
    x86_64) mkdir -pv $LFS/lib64 ;;
esac

# ============================================
# 阶段二:安装 glibc 到目标分区(仍在宿主机上)
# ============================================

cd /srv/glibc-2.37
mkdir build && cd build

../configure --prefix=/usr                      \
             --disable-profile                  \
             --enable-add-ons                   \
             --with-headers=/usr/include        \
             --with-binutils=/usr/bin

make -j$(nproc)

# 关键:DESTDIR 指定安装目标,不碰宿主机的 /lib64
make install DESTDIR=$LFS

# 此时 glibc 文件在:
#   $LFS/usr/bin/       — 工具(ldd、locale 等)
#   $LFS/lib64/         — libc.so.6、ld-linux-x86-64.so.2 等
#   $LFS/usr/include/   — 头文件
#   $LFS/usr/lib64/     — 静态库

# ============================================
# 阶段三:chroot 进去(隔离环境,安全操作)
# ============================================

# 挂载内核虚拟文件系统
mount -v --bind /dev  $LFS/dev
mount -v --bind /dev/pts $LFS/dev/pts
mount -vt proc proc  $LFS/proc
mount -vt sysfs sysfs $LFS/sys
mount -vt tmpfs tmpfs $LFS/run

# 进入 chroot
chroot "$LFS" /usr/bin/env -i   \
    HOME=/root                  \
    TERM="$TERM"                \
    PS1='(lfs chroot) \u:\w\$ ' \
    PATH=/usr/bin:/usr/sbin     \
    /bin/bash --login

# ============================================
# 阶段四:在 chroot 内完成收尾
# ============================================

# 更新动态链接器缓存
ldconfig

# 验证
ldd --version
/lib64/libc.so.6

三种方式对比:

❌ 必踩坑操作 ✅ 独立 prefix ✅ LFS chroot
configure --prefix=/usr --prefix=/opt/glibc --prefix=/usr
install 直接覆盖 直接安装 DESTDIR=$LFS
安装位置 宿主机 /lib64/ /opt/glibc/ $LFS/lib64/
系统 glibc 被覆盖,半砖 不受影响 不受影响
适用场景 工具链开发 构建独立 Linux 系统

总结--prefix 换成系统之外的路径,或者加 DESTDIR 重定向,核心都是让新 glibc 不要覆盖宿主机的 /lib64/

5.4 踩坑记录:--prefix=/usr 直接覆盖的后果及恢复

症状:make install 过程中 libc.so.6 已替换但 ld.so 未更新,导致:

gcc: relocation error: /lib64/libc.so.6: symbol __tunable_get_val,
version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2
cp: relocation error: /lib64/libpthread.so.0: symbol __libc_dl_error_tsd,
version GLIBC_PRIVATE not defined in file libc.so.6

所有动态链接命令(cp、ls、gcc)全部崩溃。

救命步骤:

你当前的 bash 进程(在安装前已启动)还能用,用 sln 补齐:

cd /srv/glibc-2.37/build

# 1. 先恢复 ld.so(最重要!)
sln elf/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2

# 2. 再补齐其他库(用 copy_sln.txt 批量操作)
sln libc.so             /lib64/libc.so.6
sln nptl/libpthread.so  /lib64/libpthread.so.0
sln math/libm.so        /lib64/libm.so.6
sln dlfcn/libdl.so      /lib64/libdl.so.2
sln rt/librt.so         /lib64/librt.so.1
sln login/libutil.so    /lib64/libutil.so.1
sln resolv/libresolv.so /lib64/libresolv.so.2
sln nss/libnss_files.so /lib64/libnss_files.so.2
sln nss/libnss_dns.so   /lib64/libnss_dns.so.2

# 3. 收尾
make install && ldconfig

5.5 验证

# 版本
/lib64/libc.so.6       # 应显示 GNU C Library stable release version 2.37
ldd --version           # 同上

# 库文件(应该是实体文件,不是软链)
ls -la /lib64/ld-linux-x86-64.so.2
ls -la /lib64/libc.so.6

# 检查系统正常
ldd /bin/ls
ls /

常见错误速查

错误 原因 解决
binutils -v 找不到命令 binutils 是工具集名,不是可执行文件 ld -v / as --version
bison configure 报 no acceptable m4 缺少 GNU M4 先编译安装 m4
glibc make install 报 __tunable_get_val relocation error libc 已更新但 ld.so 未更新 用 sln 补齐 ld.so
bzip2: Cannot exec 未安装 bzip2 yum install -y bzip2
no acceptable C compiler 未装 gcc-c++ yum install -y gcc-c++

6. RPM 封装

编译一次非常耗时,将产物打包成 RPM 可在同版本 CentOS 7 上一键安装。

6.1 前置条件

# 安装打包工具
yum install -y rpm-build rpmdevtools

# 创建 rpmbuild 目录结构
rpmdev-setuptree
# 生成目录:~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}

6.2 通用打包方法

核心思路:用 make install DESTDIR=/tmp/pkg-XXX 收集文件清单(不覆盖系统),然后写 spec 用 rpmbuild 打包。

# 1. 收集文件(以 glibc 为例)
cd /srv/glibc-2.37/build
rm -rf /tmp/pkg-glibc
make install DESTDIR=/tmp/pkg-glibc

# 2. 删除污染文件
rm -f /tmp/pkg-glibc/usr/share/info/dir

# 3. 生成文件清单(包含软链接)
files=$(find /tmp/pkg-glibc -not -type d \
    | sed 's|^/tmp/pkg-glibc||' \
    | grep -v '^/usr/share/info/dir$' \
    | sort)

# 4. 写入 spec 的 %files 段

6.3 spec 关键要点

⚠️ Provides/Conflicts 必须写在 %description 之前

RPM 的 preamble(Name, Version, Provides, Conflicts 等)必须在 %description 之前,否则会被忽略。

Name:           glibc
Version:        2.37
Provides:       rtld(GNU_HASH)          # ✅ 在 %description 前面
Conflicts:      glibc-common < 2.30

%description                              # ← preamble 在此结束
GNU C Library 2.37...
glibc 特殊处理

glibc 替换系统最底层库,需要声明大量 Provides 和 Obsoletes:

  • rtld(GNU_HASH):最关键!几乎所有系统包都依赖它,RPM 不会自动从 ld-linux-x86-64.so.2 检测到这个标记,必须手动声明
  • glibc-common/headers/devel:替代旧的子包,Obsoletes 自动卸载旧版本
  • libcidn.so.1, libnss_nis.so.2, libnss_nisplus.so.2:glibc 2.34+ 中已移除的库,旧 glibc-devel 依赖它们,需要声明空 Provides
  • locale-archive:需要从编译 VM 的 /usr/lib/locale/locale-archive 手动拷贝进 buildroot,make install DESTDIR 不会生成它
  • %post 脚本:必须用 localedef 重新注册 locale 数据
%post
if [ -x /usr/bin/localedef ] && [ -f /usr/share/i18n/locales/zh_CN ]; then
    /usr/bin/localedef -i zh_CN -f UTF-8 zh_CN.utf8 2>/dev/null || true
fi
exit 0
gcc 特殊处理
  • libgcc_s.so.1()(64bit):CentOS 7 的 RPM 自动检测会跳过这个库,必须手动声明所有 GCC_ 版本标记
  • Obsoletes:声明替代旧的 libstdc++libgcclibgomp
  • 部分组件失败make install 时 libgfortran 可能因 SIGSTKSZ 宏兼容性报错,用 make -k install(忽略错误继续)仍能收集 1500+ 文件,核心的 cc1/cc1plus/gcc/g++ 都在
Provides:       libgcc_s.so.1()(64bit)
Provides:       libgcc_s.so.1(GCC_3.0)(64bit)
Provides:       libgcc_s.so.1(GCC_3.3)(64bit)
Provides:       libgcc_s.so.1(GCC_3.3.1)(64bit)
Provides:       libgcc_s.so.1(GCC_3.4)(64bit)
Provides:       libgcc_s.so.1(GCC_4.2.0)(64bit)
Obsoletes:      libstdc++ < 10
Obsoletes:      libgcc < 10
Obsoletes:      libgomp < 10
make 特殊处理

CentOS 7 的 make 有 Epoch 1,必须声明 Epoch: 1,否则 RPM 认为 1:3.82 > 4.4

Name:           make
Epoch:          1
Version:        4.4
bison 特殊处理

make install 因 SIGSTKSZ 兼容性失败,二进制文件不会进入 DESTDIR。需要从编译 VM 的 /usr/bin/bison 手动拷贝,并创建 yacc -> bison 软链接。

m4 特殊处理

m4 源码同样不兼容 glibc 2.37 的 SIGSTKSZ。直接从系统 /usr/bin/m4/usr/share/man/man1/m4.1 手动收集即可。

6.4 构建命令

# 每个包执行(以 glibc 为例)
cd ~/rpmbuild/SPECS
rpmbuild -bb --noclean --buildroot /tmp/pkg-glibc glibc.spec

# --noclean 防止 buildroot 被清理(方便重试)
# -bb 只构建二进制 RPM,不构建 SRPM

6.5 安装(⚠️ 注意事项)

# 首次安装:一次安装全部(RPM 会自动处理依赖和顺序)
rpm -Uvh --replacefiles ./*.rpm

重要规则:

❌ 禁止 ✅ 正确做法
rpm -Uvh ./*.rpm 在已安装后再次全部重装 只重装变化的单个包
yum install glibc-langpack-zh 或任何 yum install glibc-* locale 问题用 localedef 修复
make install 替换 glibc 中途 kill rpm 进程 先用 kill -9 + rm -f /var/lib/rpm/__db.* + rpm --rebuilddb 恢复

为什么不能重装全部: glibc 的 %post 运行 ldconfig,六个 RPM 同时触发可能导致死锁。

为什么不能 yum install glibc-*: yum 源里是 CentOS 7 的 glibc 2.17,会覆盖工具链的 2.37。

6.6 locale 修复

如果安装后 locale -a 只有 C 和 POSIX:

# 原因:locale-archive 注册信息与目标系统不匹配
localedef -i zh_CN -f UTF-8 zh_CN.utf8
locale -a | grep zh_CN   # 验证

新版 glibc RPM 的 %post 已自动执行此步骤。

6.7 RPM 打包踩坑速查

错误 原因 解决
Installed (but unpackaged) file(s) found buildroot 里有文件未在 %files 中列出 补全文件清单或用 --noclean 保留 buildroot 排查
Provides 不生效 写在了 %description 之后 移到 %description 前面
glibc-common < 2.30 冲突 用了 Conflicts 改用 Obsoletes + Provides
rtld(GNU_HASH) 缺失 RPM 不自动检测 ld.so 的这个标记 手动添加 Provides: rtld(GNU_HASH)
libgcc_s.so.1 缺失 CentOS 7 RPM 不自动检测 libgcc_s 手动添加所有版本标记
make-3.82 比 make-4.4 新 系统 make 有 Epoch 1 spec 添加 Epoch: 1
重装全部 RPM 后卡死 多个 glibc %post 并发死锁 只重装变化的单个包;如果已卡死:kill -9 rpm 进程 → rm -f /var/lib/rpm/__db.*rpm --rebuilddb
locale 报"无法将 LC_ALL 设置为默认的语区" locale-archive 注册不匹配 localedef -i zh_CN -f UTF-8 zh_CN.utf8

7. 验证:实战安装 Qwen Code CLI

在升级后的 CentOS 7(glibc 2.37 + gcc 10.1.0)上安装 Qwen Code 验证工具链可用性:

# 安装 Qwen Code(Node.js 应用,依赖 glibc)
curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.sh | bash

# 加载环境变量
source ~/.bashrc

# 验证版本
qwen -v
# 输出: 0.21.10

# 启动正常
qwen

Qwen Code 是 Node.js 应用,Node 二进制动态链接 glibc。能在 glibc 2.37 上正常安装和运行,说明工具链 ABI 兼容正常,libc.so.6libpthread.so.0libdl.so.2libstdc++.so.6 等关键库工作正常。

写到最后:
如果有不想麻烦折腾编译的朋友,我这里有封装好的: https://download.csdn.net/download/dzminglong/93261163

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐