Files
2026-07-07 15:23:39 +08:00

11 KiB
Raw Permalink Blame History

可以。你现在踩到的不是单个 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/binlibc.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 里的绝对路径 /xxxld 使用 --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 是 symlinkWindows 打包时可能丢。

所以给 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

而你的工具链目录没有 ccshell 就会继续往 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 + 绝对路径

存在。

尤其是:

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