交叉编译与 QEMU 启动最小 Linux:一个“32 位内核跑 64 位程序“的启动陷阱(srvC)

📅 发布时间:2026/8/15 20:57:13
交叉编译与 QEMU 启动最小 Linux:一个“32 位内核跑 64 位程序“的启动陷阱(srvC)
交叉编译与 QEMU 启动最小 Linux一个32 位内核跑 64 位程序的启动陷阱srvC本篇基于 srvCphase5真实实操是整个系列里最值得收藏的一篇。我们交叉编译 BusyBox 与内核、制作 initramfs 根文件系统、用 QEMU 把 ARM 和 x86 的最小 Linux 都跑了起来但 x86 启动一路报error -2→error -8排查过程把initramfs 启动失败的几乎所有经典坑都踩了一遍。1. 目标与链路一条完整的在 QEMU 里跑起 Linux链路是BusyBox(根文件系统_app) Linux内核 根文件系统(initramfs) QEMU(机器模拟)ARM 侧交叉编译静态 ARM BusyBox → 编译multi_v7_defconfig内核 →vexpress-a15板型 QEMU 启动x86 侧用宿主机静态 x86_64 BusyBox → 编译tinyconfig内核 →qemu-system-x86_64启动2. ARM 链路一次成功ARM BusyBox 交叉编译关掉会导致 glibc 2.39 不兼容的CONFIG_TC静态链接sed-is/^CONFIG_TCy/# CONFIG_TC is not set/.configmakeCROSS_COMPILEarm-linux-gnueabihf--j8# 产出约 1.5M 的静态 ARM busybox可在 qemu-arm-static 下直接运行内核与设备树makeARCHarm multi_v7_defconfigmakeARCHarmCROSS_COMPILEarm-linux-gnueabihf--j8zImage# 同时得到 arch/arm/boot/dts/arm/vexpress-v2p-ca15_a7.dtbQEMU 启动vexpress-a15qemu-system-arm-Mvexpress-a15-cpucortex-a15-m512M\-kernellinux-6.6.151/arch/arm/boot/zImage\-dtbvexpress-v2p-ca15_a7.dtb\-initrdrootfs-arm.cpio.gz-appendconsolettyAMA0 rdinit/init-nographic启动后/init跑演示脚本打印进程列表并干净关机71 0 0:00 ps 演示命令执行完毕, 系统即将关机... [ 2.522735] reboot: Power downARM 侧multi_v7_defconfig默认开了BINFMT_ELF/BINFMT_SCRIPT所以脚本化的/init直接就能跑。3. x86 链路第一道坎error -2x86 侧用宿主机的静态 busybox 做根文件系统qemu-system-x86_64一启动却卡在 initRun /init as init process Failed to execute /init (error -2) Run /sbin/init as init process Starting init: /sbin/init exists but couldnt execute it (error -40) Run /etc/init as init process Run /bin/init as init process Run /bin/sh as init process Kernel panic - not syncing: No working init found. Try passing init option to kernel.error -2是ENOENT文件/解释器找不到。第一直觉是initramfs 里的软链接指错了路径。BusyBox 的busybox --install -s会在bin/、sbin/下生成大量指向 busybox 的软链接如果目标写成绝对宿主机路径如/root/lab/phase5/rootfs-x86/bin/busybox在内存根文件系统里当然找不到。抽取 cpio 检查真实目标确认确实不对修正为相对目标# bin/* 指向同目录兄弟 busyboxsbin/* 指向 ../bin/busyboxforlinbin/*;do[-L$l]ln-sfbusybox$l;doneforlinsbin/*;do[-L$l]ln-sf../bin/busybox$l;done修正后bin/sh - busybox、sbin/init - ../bin/busybox都在重打 cpio 再启动——还是失败只是报错从-2变成了-8。4. 第二道坎error -8内核没开二进制加载器-8是ENOEXEC执行格式错误。抽内核配置一看问题在tinyconfig默认把二进制加载器都关了$grep-EBINFMT_ELF|BINFMT_SCRIPTlinux-src/.config CONFIG_BINFMT_ELF# CONFIG_BINFMT_ELF is not setCONFIG_BINFMT_SCRIPT# CONFIG_BINFMT_SCRIPT is not set内核根本不认识 ELF 和#!脚本自然跑不了 busybox也跑不了/init脚本。打开它们并重建./scripts/config--enableCONFIG_BINFMT_ELF ./scripts/config--enableCONFIG_BINFMT_SCRIPTmakeolddefconfigmake-j$(nproc)bzImage再启动报错仍-8。说明还有更深的根因。5. 终极根因32 位内核 vs 64 位 BusyBox继续抽配置决定性的一条出现了$grep-ECONFIG_X86_32|CONFIG_64BITlinux-src/.configCONFIG_X86_32y# ← 内核被编成了 32 位而根文件系统里的 busybox 是64 位 ELFx86-64。一个 32 位内核去exec一个 64 位程序正是ENOEXEC——它压根不认这个格式。宿主机的chroot测试能跑主机是 64 位内核更反证了这一点$chroot/tmp/inspect_x86 /bin/sh-cecho CHROOT_OKCHROOT_OK修复把内核翻回 64 位重建 bzImagemakeclean ./scripts/config--disableCONFIG_X86_32 ./scripts/config--enableCONFIG_64BIT# 同时保留 ELF/SCRIPT/INITRD/DEVTMPFS_MOUNTmakeolddefconfigmake-j$(nproc)bzImage6. 终见曙光qemu-system-x86_64-m512M-kernellinux-src/arch/x86/boot/bzImage\-initrdrootfs-x86.cpio.gz-appendconsolettyS0 rdinit/init-nographic--- 当前进程 --- PID USER COMMAND 1 0 {init} /bin/sh /init ← /init 脚本作为 1 号进程运行 25 0 {ps} /bin/sh /init 演示命令执行完毕, 系统即将关机... reboot: System haltedx86 的最小 Linux 终于干净启动并关机。7. 排错决策树收藏initramfs 启动失败按错误码顺藤摸瓜错误码含义优先排查error -2(ENOENT)文件/解释器找不到/init软链接目标、#!解释器在 initramfs 内是否存在error -8(ENOEXEC)执行格式错误内核是否开CONFIG_BINFMT_ELF/BINFMT_SCRIPT内核位数与根文件系统里 ELF 的位数是否匹配32-bit 内核 ≠ 64-bit 程序一句话先查软链接再查BINFMT_*最后一定核对内核架构和根文件系统里程序的架构是否一致。8. 小结ARM 用multi_v7_defconfig默认配置即开箱可用x86 用tinyconfig需手动补BINFMT_ELF/SCRIPT并确认是 64 位内核initramfs 的软链接必须是相对路径、且指向 initramfs 内部存在的文件“32 位内核跑 64 位 busybox” 这种架构错配表现就是顽固的ENOEXEC靠chroot对比即可快速定位。下一篇进 srvD自己写内核模块、字符设备与 platform 总线驱动。