LFS 搭建 4 正式构建 (1)
在临时系统构建完成后,从现在开始,就进入正式构建阶段。
根据 LFS 手册,绝大多数包在编译时都不使用静态库,且一般都会在 configure 中直接关闭,不过 gcc 之类需要的除外。
在现阶段我并没有移植包管理器的打算,也许以后有时间我会出一篇将 Portage 移植到 LFS 的教程吧。 :D
进入 Chroot 环境
每次登录,都要检查$LFS
:
echo $LFS
首先先挂载/dev
和内核虚拟文件系统
1 | mount -v --bind /dev $LFS/dev |
现在开始 chroot
1 | chroot "$LFS" /usr/bin/env -i \ |
构建软件包
Man-pages-5.13
1 | tar -xvf man-pages-5.13.tar.xz |
Iana-Etc-20210611
1 | tar -xvf iana-etc-20210611.tar.gz |
这个包只需要将文件复制到/etc
下即可。
cp services protocols /etc
Glibc-2.34
这应该是最后一遍了吧。
1 | tar -xvf glibc-2.34.tar.xz |
此处需要修复一个安全问题:
1 | sed -e '/NOTIFY_REMOVED)/s/)/ \&\& data.attr != NULL)/' \ |
打补丁
patch -Np1 -i glibc-2.34-fhs-1.patch
嗯,手册现在才打 patch,可能是之前编译的都是临时用的所以不用打吧。XD
建立/build
1 | mkdir build |
确保将ldconfig
和sln
工具安装到/usr/sbin
目录中:
echo "rootsbindir=/usr/sbin" > configparms
configure
1 | ../configure --prefix=/usr \ |
编译:
make
检查:
make check
glibc 这么大的包,一共 4488 项测试,有几个错误很正常,如果只有几个错误的话忽略就行。此外手册已知会出现两个错误,分别是:
io/tst-lchmod
在 chroot 里会失败misc/tst-ttyname
在 chroot 里失败
我就报了这两个错
安装时,可能会报错/etc/ld.so.conf
找不到,此错误无害,建立个空的就行
touch /etc/ld.so.conf
还要修正 Makefile 跳过一个检查
sed '/test-installation/s@$(PERL)@echo not running@' -i ../Makefile
安装
make install
改正ldd
脚本中硬编码的可执行文件加载器路径
sed '/RTLDLIST=/s@/usr@@g' -i /usr/bin/ldd
安装nscd
的配置文件和运行时目录:
1 | cp -v ../nscd/nscd.conf /etc/nscd.conf |
安装nscd
对 systemd 的支持
1 | install -v -Dm644 ../nscd/nscd.tmpfiles /usr/lib/tmpfiles.d/nscd.conf |
现在安装 locale。
手册提供的只能用于满足测试,简体中文是没有的。因此下面的命令我加了zh_CN-UTF-8
。
所以此处会使用安装单个 locale 的指令。
1 | mkdir -pv /usr/lib/locale |
当然也可以全部安装,不过需要的时间就很长了:
make localedata/install-locales
配置 Glibc
创建/etc/nsswitch.conf
:
1 | cat > /etc/nsswitch.conf << "EOF" |
安装时区数据:
注意:此时仍在 glibc 的build
目录下,如果在其他目录要根据路径改下列命令。
1 | tar -xf ../../tzdata2021a.tar.gz |
此处顺手配置时区:
ln -sfv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
现在还需要配置动态加载器。
创建一个新的/etc/ld.so.conf
:
1 | cat > /etc/ld.so.conf << "EOF" |
不过下面的命令可能会更优雅:
1 | cat >> /etc/ld.so.conf << "EOF" |
以上两个命令可以全部使用,因为第二个命令不会擦除原ld.conf.d
的内容
Zlib-1.2.11
1 | tar -xvf zlib-1.2.11.tar.xz |
此处需要删除一些无用的静态库
rm -fv /usr/lib/libz.a
Bzip2-1.0.8
这里需要加补丁
1 | tar -xvf bzip2-1.0.8.tar.gz |
需要修改 Makefile 保证安装的符号链接是相对的。
sed -i 's@\(ln -s -f \)$(PREFIX)/bin/@\1@' Makefile
确保man
被安装到正确位置:
sed -i "s@(PREFIX)/man@(PREFIX)/share/man@g" Makefile
编译前还需要进行一些处理
1 | make -f Makefile-libbz2_so |
编译:
make
安装:
1 | make PREFIX=/usr install |
此处对其共享库进行了一些处理
删除静态库
rm -fv /usr/lib/libbz2.a
Xz-5.2.5
1 | tar -xvf xz-5.2.5.tar.xz |
configure
1 | ./configure --prefix=/usr \ |
编译,检查和安装
1 | make |
Zstd-1.5.0
1 | tar -xvf zstd-1.5.0.tar.gz |
如果编译时出现 “failed” 时,那没有问题,只有 “FAIL”才是失败。但测试需要全部通过。
安装
make prefix=/usr install
删除静态库
rm -v /usr/lib/libzstd.a
File-5.40
1 | tar -xvf file-5.40.tar.gz |
Readline-8.1
1 | tar -xvf readline-8.1.tar.gz |
重新安装Readline
会导致旧版本的库被重命名为<库名称>.old
。这一般不是问题,但某些情况下会触发ldconfig
的一个链接 bug。运行下面的两条 sed 命令防止这种情况:
1 | sed -i '/MV.*old/d' Makefile.in |
configure
1 | ./configure --prefix=/usr \ |
编译和安装:
1 | make SHLIB_LIBS="-lncursesw" |
安装文档(可选):
install -v -m644 doc/*.{ps,pdf,html,dvi} /usr/share/doc/readline-8.1
M4-1.4.19
1 | tar -xvf m4-1.4.19.tar.xz |
Bc-5.0.0
1 | tar -xvf bc-5.0.0.tar.xz |
这个
O3
就很灵性
Flex-2.6.4
1 | tar -xvf flex-2.6.4.tar.gz |
configure
1 | ./configure --prefix=/usr \ |
编译,检查和安装
1 | make |
添加一个lex
到flex
的符号链接
ln -sv flex /usr/bin/lex
Tcl-8.6.11
这是三个需要用到的测试套件其中之一。
进入安装目录后,要解压文档,在解压源码时要注意区分。
1 | tar -xvf tcl8.6.11-src.tar.gz |
这里的 configure 有点特殊
1 | SRCDIR=$(pwd) |
编译,也有点特殊
1 | make |
测试
make test
已知测试unitInit-1.2
会失败。
但我测试时全部成功了
安装,并修改安装好的库的权限:
1 | make install |
安装头文件,这是等一会要安装的expect
的依赖
make install-private-headers
创建符号链接
ln -sfv tclsh8.6 /usr/bin/tclsh
最后修改一个与 Perl man 冲突的错误:
mv /usr/share/man/man3/{Thread,Tcl_Thread}.3
Expect-5.45.4
三个测试套件之二。
1 | tar -xvf expect5.45.4.tar.gz |
configure
1 | ./configure --prefix=/usr \ |
编译,测试
1 | make |
安装
1 | make install |
DejaGNU-1.6.3
三个测试套件之三。
此处需要build
1 | tar -xvf dejagnu-1.6.3.tar.gz |
configure
1 | ../configure --prefix=/usr |
直接安装
1 | make install |
当然也可以测试,虽然没有必要
make check
Binutils-2.37
这应该是最后一遍了吧。
1 | tar -xvf binutils-2.37.tar.xz |
同样需要对 pty 进行测试,该命令应返回spawn ls
:
expect -c "spawn ls"
手册在这里打补丁,其实在进入目录后就可以打:
patch -Np1 -i binutils-2.37-upstream_fix-1.patch
绕过关于man
的一个问题:
1 | sed -i '63d' etc/texi2pod.pl |
创建build
:
1 | mkdir -v build |
configure
1 | ../configure --prefix=/usr \ |
编译及测试
1 | make tooldir=/usr |
一定要运行测试!
已知四项与zlib
有关的测试会失败。
嗯,我全中。
安装,删除无用的静态库
1 | make tooldir=/usr install -j1 |
GMP-6.2.1
1 | tar -xvf gmp-6.2.1.tar.xz |
如果宿主机硬件是 64 位但安装的 OS 是 32 位,还有CFLAGS
变量时,需要运行
ABI=32 ./configure ...
取消处理器优化,生成通用库。(可选,且如果宿主机 CPU 与目标机一致就不用选,在 live CD 里编译也不用。)
1 | cp -v configfsf.guess config.guess |
configure
1 | ./configure --prefix=/usr \ |
编译并生成文档
1 | make |
测试:
一定要测试!
因为 GMP 编译时是针对 CPU 高度优化的,有时会错误识别 CPU 功能导致测试时大概率出现一堆Illegal instruction
。此时就需要重新编译并加上--build=x86_64-pc-linux-gnu
。
以下命令查看通过测试的数量,GMP 一共 197 项测试,务必全部通过。
awk '/# PASS:/{total+=$3} ; END{print total}' gmp-check-log
安装,包括文档
1 | make install |
MPFR-4.1.0
1 | tar -xvf mpfr-4.1.0.tar.xz |
configure
1 | ./configure --prefix=/usr \ |
编译并生成文档:
1 | make |
测试:
一定要测试!
make check
确保全部通过。
安装
1 | make install |
MPC-1.2.1
1 | tar -xvf mpc-1.2.1.tar.gz |
configure
1 | ./configure --prefix=/usr \ |
编译,测试和安装
1 | make |
Attr-2.5.1
1 | tar -xvf attr-2.5.1.tar.gz |
configure
1 | ./configure --prefix=/usr \ |
编译,测试和安装
1 | make |
Acl-2.3.1
1 | tar -xvf acl-2.3.1.tar.xz |
configure
1 | ./configure --prefix=/usr \ |
编译和安装:
1 | make |
acl
的测试依赖于连接了 Acl 的Coreutils,如果要测试,应在 Coreutils 构建完成后进行。
因此,构建完成后源码目录暂不删除。
Libcap-2.53
1 | tar -xvf libcap-2.53.tar.xz |
防止安装静态库:
sed -i '/install -m.*STA/d' libcap/Makefile
编译,测试,安装
1 | make prefix=/usr lib=lib |
修改权限
chmod -v 755 /usr/lib/lib{cap,psx}.so.2.53
Shadow-4.9
1 | tar -xvf shadow-4.9.tar.xz |
禁止该包安装groups
程序和它的 man 页面,因为 Coreutils 会提供更好的版本。
1 | sed -i 's/groups$(EXEEXT) //' src/Makefile.in |
不使用默认的 crypt 加密方法,使用更安全的 SHA-512 方法加密密码,该方法也允许长度超过 8 个字符的密码。还需要把用户邮箱位置/var/spool/mail
改为/var/mail
。另外,从默认的PATH
中删除/bin
和/sbin
,因为它们只是指向/usr
中对应目录的符号链接:
1 | sed -e 's:#ENCRYPT_METHOD DES:ENCRYPT_METHOD SHA512:' \ |
修复程序中的一处低级错误:
sed -e "224s/rounds/min_rounds/" -i libmisc/salt.c
configure
1 | touch /usr/bin/passwd |
编译:
make
安装:
1 | make exec_prefix=/usr install |
配置
启用用户密码的加密
pwconv
启用组密码加密
grpconv
shadow
带了一个useradd
的默认配置文件,其中会为新用户创建邮箱文件,若关闭,运行
sed -i 's/yes/no/' /etc/default/useradd
设置 root 密码
passwd root
GCC-11.2.0
这一遍需要巨长的时间。
1 | tar -xvf gcc-11.2.0.tar.xz |
修复在使用 Glibc-2.34 的系统上构建该软件包时导致libasan.a
无法使用的问题:
1 | sed -e '/static.*SIGSTKSZ/d' \ |
修改 64 位库默认路径
1 | case $(uname -m) in |
创建build
1 | mkdir -v build |
configure
1 | ../configure --prefix=/usr \ |
编译:
make
增加栈空间
ulimit -s 32768
以非特权用户身份测试编译结果,但出错时继续执行其他测试:
1 | chown -Rv tester . |
查看摘要
../contrib/test_summary
已知错误如下:
- 已知八项与
analyzer
相关的测试会失败。 - 已知一项名为
asan_test.C
的测试会失败。 - 在
libstdc++
中,已知一项名为49745.cc
的测试由于 Glibc 中头文件依赖关系的变化而失败。 - 在
libstdc++
测试中,一项numpuct
测试和六项与 get_time 有关的测试会失败。这是由于 glibc 更新了 locale 定义,但是 libstdc++ 尚不支持这些变化。
同时,还有少量错误是十分正常的,毕竟这是 gcc。 (
哪怕测试时间比编译时间还要长十倍也要坚持测试!
但时间真的太长了……我跑了近 5 个小时,然而编译只需要 30 分钟不到
安装该软件包,并移除一个不需要的目录:
1 | make install |
修正之前因为测试而临时更改的文件所有者
1 | chown -v -R root:root \ |
创建一个符号链接
1 | ln -svr /usr/bin/cpp /usr/lib |
第二次检查
现在工具链已经完成安装,需要进行一次完整的确认。
离开源码目录,然后
1 | echo 'int main(){}' > dummy.c |
如果正常,输出的最后一行会是(具体名称取决于平台)
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
然后
grep -o '/usr/lib.*/crt[1in].*succeeded' dummy.log
正常输出应该是这样:
1 | /usr/lib/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../lib/crt1.o succeeded |
gcc
应该找到所有三个crt*.o
文件,它们应该位于/usr/lib
目录中。
确认编译器能正确查找头文件:
grep -B4 '^ /usr/include' dummy.log
这是正常输出:
1 | #include <...> search starts here: |
其中三元组的名称取决于平台。
确认新的链接器使用了正确的搜索路径:
grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
路径应该要包含
1 | SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib64") |
当然 32 位的路径会有不同。
确认使用了正确的 libc:
grep "/lib.*/libc.so.6 " dummy.log
应该输出
attempt to open /usr/lib/libc.so.6 succeeded
确认 GCC 使用了正确的动态链接器:
grep found dummy.log
正常输出:
found ld-linux-x86-64.so.2 at /usr/lib/ld-linux-x86-64.so.2
如果有问题,一定要修复,不能硬着头皮往下做,不然更折腾人。
如果一切正常,删除测试文件:
rm -v dummy.c a.out dummy.log
最后移动一个位置不正确的文件:
1 | mkdir -pv /usr/share/gdb/auto-load/usr/lib |
Pkg-config-0.29.2
1 | tar -xvf pkg-config-0.29.2.tar.gz |
configure
1 | ./configure --prefix=/usr \ |
编译,检查,安装
1 | make |
Ncurses-6.2
1 | tar -xvf ncurses-6.2.tar.gz |
configure
1 | ./configure --prefix=/usr \ |
编译,安装
1 | make |
这个包有测试,但需要安装后面的包,所以跳过。
创建符号链接:
1 | for lib in ncurses form panel menu ; do |
删除一个 configure 脚本未处理的静态库:
rm -fv /usr/lib/libncurses++w.a
安装文档(可选):
1 | mkdir -v /usr/share/doc/ncurses-6.2 |
注意:此处没有创建非宽字符的库,部分二进制包会依赖于它。
Sed-4.8
1 | tar -xvf sed-4.8.tar.xz |
测试:
1 | chown -Rv tester . |
我在检查时会报错:
inplace-selinux.sh: set-up failure: CONFIG_HEADER not defined
,看上去与 selinux 有关,不用 selinux 应该不受影响。
但具体原因未知。
安装:
1 | make install |
Psmisc-23.4
1 | tar -xvf psmisc-23.4.tar.xz |
Gettext-0.21
1 | tar -xvf gettext-0.21.tar.xz |
configure
1 | ./configure --prefix=/usr \ |
编译,检查
1 | make |
测试时间会和编译时间一样长。
安装:
1 | make install |
Bison-3.7.6
1 | tar -xvf bison-3.7.6.tar.xz |
可以测试,不过时间有点长。(可选):
make check
Grep-3.7
1 | tar -xvf grep-3.7.tar.xz |
Bash-5.1.8
1 | tar -xvf bash-5.1.8.tar.gz |
configure
1 | ./configure --prefix=/usr \ |
编译:
make
测试(可选):
1 | chown -Rv tester . |
安装:
make install
换 shell:
exec /bin/bash --login +h
Libtool-2.4.6
1 | tar -xvf libtool-2.4.6.tar.xz |
测试,这里建议打开多线程:
make check TESTSUITEFLAGS=-j5
这里的线程数一般为逻辑 CPU 数 + 1。
已知会有5个因循环依赖导致的失败,但安装 automake 后可通过。
安装,删除静态库:
1 | make install |
GDBM-1.20
1 | tar -xvf gdbm-1.20.tar.gz |
configure
1 | ./configure --prefix=/usr \ |
编译,测试:
1 | make |
已知gdbmtool
测试会失败。
安装:
make install
Gperf-3.1
1 | tar -xvf gperf-3.1.tar.gz |
这里开-j1
是因为多线程会导致错误。
Expat-2.4.1
1 | tar -xvf expat-2.4.1.tar.xz |
configure
1 | ./configure --prefix=/usr \ |
编译,检查,安装:
1 | make |
安装文档(可选):
install -v -m644 doc/*.{html,png,css} /usr/share/doc/expat-2.4.1
至此,软件包安装完成过半。
剩下的下篇再继续。
76 个包,属实是体力活……
LFS 搭建 4 正式构建 (1)
https://williamgong.github.io/2021/11/30/lfs%E6%90%AD%E5%BB%BA4/