540 lines
11 KiB
Markdown
540 lines
11 KiB
Markdown
可以。你现在踩到的不是单个 `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` 只是:
|
||
|
||
```bash
|
||
把 deb 里的文件倒出来
|
||
```
|
||
|
||
不是:
|
||
|
||
```bash
|
||
完整安装一个 Ubuntu 包
|
||
```
|
||
|
||
---
|
||
|
||
# 2. sysroot 里必须同时有“运行库”和“开发文件”
|
||
|
||
你现在做 GCC/CMake 编译 sysroot,至少要分清这几类包。
|
||
|
||
## 2.1 `libc6`
|
||
|
||
提供 glibc 运行库,通常包含:
|
||
|
||
```text
|
||
/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 文件。
|
||
|
||
你的错误:
|
||
|
||
```text
|
||
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 编译开发文件,通常包含:
|
||
|
||
```text
|
||
/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
|
||
```
|
||
|
||
注意:
|
||
|
||
```text
|
||
/usr/lib/x86_64-linux-gnu/libc.so
|
||
```
|
||
|
||
通常不是 ELF 动态库,而是 **ld linker script**。
|
||
|
||
里面会写类似:
|
||
|
||
```text
|
||
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 )
|
||
)
|
||
```
|
||
|
||
所以即使你有:
|
||
|
||
```text
|
||
/usr/lib/x86_64-linux-gnu/libc.so.6
|
||
```
|
||
|
||
也不够。因为 `libc.so` 脚本明确让链接器去找:
|
||
|
||
```text
|
||
/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:
|
||
|
||
```text
|
||
/usr/include/linux/*.h
|
||
/usr/include/asm-generic/*.h
|
||
/usr/include/x86_64-linux-gnu/asm/*.h
|
||
```
|
||
|
||
这个是编译很多 Linux 程序需要的头文件,不提供 glibc runtime。
|
||
|
||
---
|
||
|
||
# 3. `/usr/lib` 和 `/lib` 不是可以随便互换的
|
||
|
||
你截图里有:
|
||
|
||
```text
|
||
sysroot/usr/lib/x86_64-linux-gnu/libc.so.6
|
||
```
|
||
|
||
但链接器要的是:
|
||
|
||
```text
|
||
sysroot/lib/x86_64-linux-gnu/libc.so.6
|
||
```
|
||
|
||
这是两个路径。
|
||
|
||
在真实 Ubuntu 系统里,发行版可能通过以下方式保证它们都正常:
|
||
|
||
```text
|
||
/lib 是真实目录
|
||
或者 /lib -> usr/lib
|
||
或者包里带了软链接
|
||
或者 loader/linker script 指向固定路径
|
||
```
|
||
|
||
但你自己裁剪 sysroot、再打包到 Windows 时,最容易丢的是:
|
||
|
||
```text
|
||
/lib
|
||
/lib64
|
||
软链接
|
||
绝对 symlink
|
||
```
|
||
|
||
尤其 WinRAR 会提示“跳过可能不安全的链接”,这就会破坏 Linux sysroot。
|
||
|
||
---
|
||
|
||
# 4. `/lib64` 也可能必须存在
|
||
|
||
x86_64 glibc 的动态加载器有时通过这些路径出现:
|
||
|
||
```text
|
||
/lib64/ld-linux-x86-64.so.2
|
||
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||
```
|
||
|
||
具体看你的:
|
||
|
||
```bash
|
||
cat "$sysroot/usr/lib/x86_64-linux-gnu/libc.so"
|
||
```
|
||
|
||
只要 `libc.so` 里写了某个绝对路径,例如:
|
||
|
||
```text
|
||
/lib64/ld-linux-x86-64.so.2
|
||
```
|
||
|
||
那么 sysroot 里就必须有:
|
||
|
||
```text
|
||
$sysroot/lib64/ld-linux-x86-64.so.2
|
||
```
|
||
|
||
只要它写的是:
|
||
|
||
```text
|
||
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||
```
|
||
|
||
那么 sysroot 里就必须有:
|
||
|
||
```text
|
||
$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`。真正要看这个:
|
||
|
||
```bash
|
||
cat "$sysroot/usr/lib/x86_64-linux-gnu/libc.so"
|
||
```
|
||
|
||
然后把里面所有 `/lib/...`、`/usr/lib/...`、`/lib64/...` 的绝对路径都补齐。
|
||
|
||
可以用这个检查:
|
||
|
||
```bash
|
||
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 至少应该有:
|
||
|
||
```text
|
||
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 或工具链需要
|
||
```
|
||
|
||
另外常见还需要:
|
||
|
||
```text
|
||
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
|
||
|
||
你的白名单应该保留:
|
||
|
||
```bash
|
||
--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 工具链发布用,建议最后做一次“实体化”:
|
||
|
||
```bash
|
||
sudo cp -aL "$src" "$dst"
|
||
```
|
||
|
||
`-L` 的意义是:如果源文件是软链接,就复制软链接指向的真实文件。
|
||
|
||
---
|
||
|
||
# 8. `cc` 也是发行版隐式行为
|
||
|
||
你前面遇到的 `cc 9.4` 也是同一类问题。
|
||
|
||
Ubuntu 发行版通常提供:
|
||
|
||
```text
|
||
/usr/bin/cc -> /etc/alternatives/cc -> /usr/bin/gcc
|
||
```
|
||
|
||
但你自己 `make install` GCC 时,通常只生成:
|
||
|
||
```text
|
||
gcc
|
||
g++
|
||
c++
|
||
cpp
|
||
```
|
||
|
||
不一定生成:
|
||
|
||
```text
|
||
cc
|
||
```
|
||
|
||
所以如果 configure 里传了:
|
||
|
||
```text
|
||
CC=cc
|
||
```
|
||
|
||
而你的工具链目录没有 `cc`,shell 就会继续往 PATH 后面找,最后找到系统 `/usr/bin/cc`。
|
||
|
||
这也是发行版帮你做了 `update-alternatives`,你自己的工具链没有。
|
||
|
||
---
|
||
|
||
# 9. `dpkg-deb -x` 方式的正确认知
|
||
|
||
你现在这种函数:
|
||
|
||
```bash
|
||
install_iso_deb_to_sysroot_by_name "libc6-dev" ...
|
||
```
|
||
|
||
本质是:
|
||
|
||
```text
|
||
从 ISO pool 找 deb -> 解包文件 -> 放入 sysroot
|
||
```
|
||
|
||
它不会自动安装依赖。
|
||
|
||
所以如果你解 `libc6-dev`,它可能依赖:
|
||
|
||
```text
|
||
libc6
|
||
linux-libc-dev
|
||
```
|
||
|
||
但它不会帮你自动解这两个包。
|
||
|
||
而 Live ISO 的 `/pool` 里不一定有 `libc6`,因为 `libc6` 已经在:
|
||
|
||
```text
|
||
casper/filesystem.squashfs
|
||
```
|
||
|
||
里面了。
|
||
|
||
所以正确策略是:
|
||
|
||
```text
|
||
libc6:优先从已解出的 rootfs 复制/修正
|
||
libc6-dev:从 ISO pool 解包
|
||
linux-libc-dev:从 ISO pool 解包
|
||
```
|
||
|
||
---
|
||
|
||
# 10. 你应该采用的最终流程
|
||
|
||
## 第一步:unsquashfs 提取完整 rootfs
|
||
|
||
```bash
|
||
sudo unsquashfs -d /ae/sysroots/ubuntu2004 /media/wyc/Ubuntu\ 20.04.6\ LTS\ amd64/casper/filesystem.squashfs
|
||
```
|
||
|
||
这个里面通常已经有:
|
||
|
||
```text
|
||
/lib/x86_64-linux-gnu/libc.so.6
|
||
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||
```
|
||
|
||
## 第二步:从 ISO pool 解包开发包
|
||
|
||
```bash
|
||
libc6-dev
|
||
linux-libc-dev
|
||
```
|
||
|
||
## 第三步:生成精简 sysroot
|
||
|
||
只复制这些:
|
||
|
||
```text
|
||
/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 绝对路径
|
||
|
||
检查:
|
||
|
||
```bash
|
||
cat "$sysroot/usr/lib/x86_64-linux-gnu/libc.so"
|
||
```
|
||
|
||
凡是里面写的绝对路径,必须在:
|
||
|
||
```text
|
||
$sysroot + 绝对路径
|
||
```
|
||
|
||
存在。
|
||
|
||
## 第五步:Windows 打包前实体化 symlink
|
||
|
||
尤其是:
|
||
|
||
```text
|
||
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 跑:
|
||
|
||
```bash
|
||
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. 一句话总结
|
||
|
||
发行版隐式帮你维护了:
|
||
|
||
```text
|
||
依赖关系
|
||
multiarch 路径
|
||
/lib 与 /usr/lib 布局
|
||
/lib64 loader
|
||
glibc linker script 引用路径
|
||
软链接
|
||
cc alternatives
|
||
ldconfig/triggers
|
||
```
|
||
|
||
你现在不是在“正常安装 Ubuntu”,而是在做一个 **可发布到 Windows 的精简 Linux sysroot**。因此必须自己保证:
|
||
|
||
```text
|
||
linker script 里引用的绝对路径,在 sysroot 里真实存在
|
||
```
|
||
|
||
尤其是:
|
||
|
||
```text
|
||
sysroot/lib/x86_64-linux-gnu/libc.so.6
|
||
sysroot/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||
```
|
||
|
||
不能只看:
|
||
|
||
```text
|
||
sysroot/usr/lib/x86_64-linux-gnu/libc.so.6
|
||
```
|