11 KiB
可以。你现在踩到的不是单个 libc6 问题,而是 Debian/Ubuntu 发行版安装系统时自动维护了一整套隐式约定。你自己做精简 sysroot 时,这些隐式行为都要手动还原,否则 GCC/ld/CMake 会报各种“明明有文件但找不到”的错误。
下面一次说完整。
1. 发行版真正安装包时做了什么
Ubuntu 不是简单把 .deb 文件解压到 /。正常 apt install / dpkg -i 会做这些事:
| 行为 | 说明 | 你用 dpkg-deb -x 是否会做 |
|---|---|---|
| 解包文件 | 把 deb 里的文件释放到根目录 | 会 |
| 解析依赖 | 自动安装 Depends 包 |
不会 |
执行 preinst/postinst |
包安装前后脚本 | 不会 |
| 执行 trigger | 例如 ldconfig、icon cache、man-db |
不会 |
| 维护 dpkg 数据库 | /var/lib/dpkg/status |
不会 |
| 创建/更新 alternatives | 例如 /usr/bin/cc 指向 gcc |
不会 |
| 保持 symlink 布局 | /lib、/bin、libc.so.6 等软链接 |
只解包原样;跨 Windows 打包可能丢 |
| 维护动态库缓存 | /etc/ld.so.cache |
不会 |
| 按 multiarch 放路径 | /usr/lib/x86_64-linux-gnu、/lib/x86_64-linux-gnu |
文件本身会解包,但你裁剪时可能漏 |
所以 dpkg-deb -x 只是:
把 deb 里的文件倒出来
不是:
完整安装一个 Ubuntu 包
2. sysroot 里必须同时有“运行库”和“开发文件”
你现在做 GCC/CMake 编译 sysroot,至少要分清这几类包。
2.1 libc6
提供 glibc 运行库,通常包含:
/lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/libm.so.6
/lib/x86_64-linux-gnu/libpthread.so.0
/lib/x86_64-linux-gnu/libdl.so.2
/lib/x86_64-linux-gnu/librt.so.1
这些是运行期 ELF 文件。
你的错误:
cannot find /lib/x86_64-linux-gnu/libc.so.6 inside sysroot
cannot find /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 inside sysroot
就是缺这类文件,或者这些文件在错误位置。
2.2 libc6-dev
提供 glibc 编译开发文件,通常包含:
/usr/include/stdio.h
/usr/include/features.h
/usr/lib/x86_64-linux-gnu/crt1.o
/usr/lib/x86_64-linux-gnu/crti.o
/usr/lib/x86_64-linux-gnu/crtn.o
/usr/lib/x86_64-linux-gnu/Scrt1.o
/usr/lib/x86_64-linux-gnu/rcrt1.o
/usr/lib/x86_64-linux-gnu/libc.so
/usr/lib/x86_64-linux-gnu/libc_nonshared.a
注意:
/usr/lib/x86_64-linux-gnu/libc.so
通常不是 ELF 动态库,而是 ld linker script。
里面会写类似:
GROUP (
/lib/x86_64-linux-gnu/libc.so.6
/usr/lib/x86_64-linux-gnu/libc_nonshared.a
AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 )
)
所以即使你有:
/usr/lib/x86_64-linux-gnu/libc.so.6
也不够。因为 libc.so 脚本明确让链接器去找:
/lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
2.3 linux-libc-dev
提供 Linux UAPI headers:
/usr/include/linux/*.h
/usr/include/asm-generic/*.h
/usr/include/x86_64-linux-gnu/asm/*.h
这个是编译很多 Linux 程序需要的头文件,不提供 glibc runtime。
3. /usr/lib 和 /lib 不是可以随便互换的
你截图里有:
sysroot/usr/lib/x86_64-linux-gnu/libc.so.6
但链接器要的是:
sysroot/lib/x86_64-linux-gnu/libc.so.6
这是两个路径。
在真实 Ubuntu 系统里,发行版可能通过以下方式保证它们都正常:
/lib 是真实目录
或者 /lib -> usr/lib
或者包里带了软链接
或者 loader/linker script 指向固定路径
但你自己裁剪 sysroot、再打包到 Windows 时,最容易丢的是:
/lib
/lib64
软链接
绝对 symlink
尤其 WinRAR 会提示“跳过可能不安全的链接”,这就会破坏 Linux sysroot。
4. /lib64 也可能必须存在
x86_64 glibc 的动态加载器有时通过这些路径出现:
/lib64/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
具体看你的:
cat "$sysroot/usr/lib/x86_64-linux-gnu/libc.so"
只要 libc.so 里写了某个绝对路径,例如:
/lib64/ld-linux-x86-64.so.2
那么 sysroot 里就必须有:
$sysroot/lib64/ld-linux-x86-64.so.2
只要它写的是:
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
那么 sysroot 里就必须有:
$sysroot/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
规则很简单:linker script 里的绝对路径 /xxx,ld 使用 --sysroot 后会找 $sysroot/xxx。
5. /usr/lib/x86_64-linux-gnu/libc.so 是关键
你不要只看有没有 libc.so.6。真正要看这个:
cat "$sysroot/usr/lib/x86_64-linux-gnu/libc.so"
然后把里面所有 /lib/...、/usr/lib/...、/lib64/... 的绝对路径都补齐。
可以用这个检查:
target=x86_64-linux-gnu
sysroot=/ae/sysroots/ubuntu2004
echo "[INFO] libc.so linker script:"
cat "$sysroot/usr/lib/$target/libc.so"
echo "[INFO] Absolute paths referenced by libc.so:"
grep -oE '/(lib64|lib|usr/lib)[^ )]+' "$sysroot/usr/lib/$target/libc.so" | sort -u
echo "[INFO] Check referenced files:"
grep -oE '/(lib64|lib|usr/lib)[^ )]+' "$sysroot/usr/lib/$target/libc.so" | sort -u | while read -r p; do
if [ -e "$sysroot$p" ]; then
echo "[OK] $sysroot$p"
else
echo "[MISS] $sysroot$p"
fi
done
这个比猜测更可靠。
6. 你这种精简 sysroot 必须手动还原的东西
最终 sysroot 至少应该有:
usr/include/stdio.h
usr/include/features.h
usr/include/linux
usr/include/asm-generic
usr/include/x86_64-linux-gnu/asm
usr/lib/x86_64-linux-gnu/crt1.o
usr/lib/x86_64-linux-gnu/crti.o
usr/lib/x86_64-linux-gnu/crtn.o
usr/lib/x86_64-linux-gnu/Scrt1.o
usr/lib/x86_64-linux-gnu/rcrt1.o
usr/lib/x86_64-linux-gnu/libc.so
usr/lib/x86_64-linux-gnu/libc_nonshared.a
lib/x86_64-linux-gnu/libc.so.6
lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
lib64/ld-linux-x86-64.so.2 # 如果 libc.so 或工具链需要
另外常见还需要:
lib/x86_64-linux-gnu/libm.so.6
lib/x86_64-linux-gnu/libpthread.so.0
lib/x86_64-linux-gnu/libdl.so.2
lib/x86_64-linux-gnu/librt.so.1
lib/x86_64-linux-gnu/libresolv.so.2
lib/x86_64-linux-gnu/libgcc_s.so.1
但 libgcc_s.so.1 最好来自你的 GCC 工具链,不要混旧系统版本,除非你明确知道搜索顺序。
7. 你之前的 rsync 白名单还要注意 symlink
你的白名单应该保留:
--include='/lib/'
--include="/lib/${target}/***"
--include='/lib64/***'
--include='/usr/'
--include='/usr/include/***'
--include='/usr/lib/'
--include="/usr/lib/${target}/***"
--include='/usr/lib/gcc/'
--include="/usr/lib/gcc/${target}/***"
--exclude='*'
但问题是:如果 /lib 是 symlink,或者里面的 libc.so.6 是 symlink,Windows 打包时可能丢。
所以给 Windows 工具链发布用,建议最后做一次“实体化”:
sudo cp -aL "$src" "$dst"
-L 的意义是:如果源文件是软链接,就复制软链接指向的真实文件。
8. cc 也是发行版隐式行为
你前面遇到的 cc 9.4 也是同一类问题。
Ubuntu 发行版通常提供:
/usr/bin/cc -> /etc/alternatives/cc -> /usr/bin/gcc
但你自己 make install GCC 时,通常只生成:
gcc
g++
c++
cpp
不一定生成:
cc
所以如果 configure 里传了:
CC=cc
而你的工具链目录没有 cc,shell 就会继续往 PATH 后面找,最后找到系统 /usr/bin/cc。
这也是发行版帮你做了 update-alternatives,你自己的工具链没有。
9. dpkg-deb -x 方式的正确认知
你现在这种函数:
install_iso_deb_to_sysroot_by_name "libc6-dev" ...
本质是:
从 ISO pool 找 deb -> 解包文件 -> 放入 sysroot
它不会自动安装依赖。
所以如果你解 libc6-dev,它可能依赖:
libc6
linux-libc-dev
但它不会帮你自动解这两个包。
而 Live ISO 的 /pool 里不一定有 libc6,因为 libc6 已经在:
casper/filesystem.squashfs
里面了。
所以正确策略是:
libc6:优先从已解出的 rootfs 复制/修正
libc6-dev:从 ISO pool 解包
linux-libc-dev:从 ISO pool 解包
10. 你应该采用的最终流程
第一步:unsquashfs 提取完整 rootfs
sudo unsquashfs -d /ae/sysroots/ubuntu2004 /media/wyc/Ubuntu\ 20.04.6\ LTS\ amd64/casper/filesystem.squashfs
这个里面通常已经有:
/lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
第二步:从 ISO pool 解包开发包
libc6-dev
linux-libc-dev
第三步:生成精简 sysroot
只复制这些:
/usr/include
/usr/lib/x86_64-linux-gnu
/lib/x86_64-linux-gnu
/lib64
/etc/ld.so.conf
/etc/ld.so.conf.d
第四步:修正 glibc runtime 绝对路径
检查:
cat "$sysroot/usr/lib/x86_64-linux-gnu/libc.so"
凡是里面写的绝对路径,必须在:
$sysroot + 绝对路径
存在。
第五步:Windows 打包前实体化 symlink
尤其是:
libc.so.6
ld-linux-x86-64.so.2
libm.so.6
libpthread.so.0
libdl.so.2
librt.so.1
libresolv.so.2
不要让 WinRAR 跳过软链接。
11. 最终检查命令
在 Linux 打包前,对最终 sysroot 跑:
target=x86_64-linux-gnu
sysroot=/path/to/final/sysroot
check() {
if [ -e "$sysroot/$1" ]; then
echo "[OK] $1"
else
echo "[MISS] $1"
fi
}
check "usr/include/stdio.h"
check "usr/include/features.h"
check "usr/lib/$target/crt1.o"
check "usr/lib/$target/crti.o"
check "usr/lib/$target/crtn.o"
check "usr/lib/$target/Scrt1.o"
check "usr/lib/$target/libc.so"
check "usr/lib/$target/libc_nonshared.a"
check "lib/$target/libc.so.6"
check "lib/$target/ld-linux-x86-64.so.2"
if [ -e "$sysroot/usr/lib/$target/libc.so" ]; then
echo "[INFO] Checking absolute references in libc.so:"
grep -oE '/(lib64|lib|usr/lib)[^ )]+' "$sysroot/usr/lib/$target/libc.so" | sort -u | while read -r p; do
if [ -e "$sysroot$p" ]; then
echo "[OK] $p"
else
echo "[MISS] $p"
fi
done
fi
这个检查能直接解释你 CMake 那种错误。
12. 一句话总结
发行版隐式帮你维护了:
依赖关系
multiarch 路径
/lib 与 /usr/lib 布局
/lib64 loader
glibc linker script 引用路径
软链接
cc alternatives
ldconfig/triggers
你现在不是在“正常安装 Ubuntu”,而是在做一个 可发布到 Windows 的精简 Linux sysroot。因此必须自己保证:
linker script 里引用的绝对路径,在 sysroot 里真实存在
尤其是:
sysroot/lib/x86_64-linux-gnu/libc.so.6
sysroot/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
不能只看:
sysroot/usr/lib/x86_64-linux-gnu/libc.so.6