我正在尝试为一个可以从同一内核运行两种字节序的系统设置多架构根文件系统。当然,我不能在同一个程序中混合使用不同字节序的库。最重要的是,大字节序根文件系统与 µClibc 及其动态加载器链接,而小字节序版本与 glibc 链接。
我不知道众所周知的与运行 i686 和 amd64 的系统如何分离(即使他们很少使用不同的字节顺序)。
但我的情况在某些方面看起来很相似......这意味着:
- 我可以使用 chroot 切换到不同的系统: (
chroot /tmp/rootfs
) - 静态编译的二进制文件可以在任何地方运行;
²
example.c:
int main() {return 200;}
²
localhost# gcc -static -Ofast $HOME/example.c -o $HOME/example
localhost# $HOME/example
localhost# echo $?
200
localhost# mv $HOME/example /tmp/rootfs
localhost# chroot /tmp/rootfs /example
localhost# echo $?
200
因此我在 /etc/ld.so.conf 中添加了以下路径:
/tmp/rootfs/lib
/tmp/rootfs/usr/lib
我从其他 rootfs 复制了动态加载器:(µClibc 使用 ld-uClibc.so)
localhost# ln /tmp/rootfs/lib/ld-linux.so.2 /lib/
localhost# ln /tmp/rootfs/bin/zsh5 /bin/
ld-linux.so.2 是一个静态二进制文件,所以我可以用它运行任何动态链接库。
localhost# /lib/ld-linux.so.2 /bin/zsh5
localhost# exit
localhost#
它确实看起来不错,但是 portage 的脚本无法通过这种方式工作。我不能使用 binfmt,因为它会创建一个循环,因为 /lib/ld-linux.so.2 具有与目标相同的体系结构。
所以我以为内核会自动找到并使用正确的 ELF 解释器,但事实并非如此:
localhost# zsh5
/bin/zsh5: No such file or directory
我仍然可以运行 µClibc 可执行文件:
localhost# busybox
BusyBox v1.22.1 (2014-06-11 08:01:31 UTC) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2012.
Licensed under GPLv2. See source distribution for detailed
copyright notices.
Usage: busybox [function [arguments]...]
or: busybox --list[-full]
or: busybox --install [-s] [DIR]
or: function [arguments]...
BusyBox is a multi-call binary that combines many common Unix
utilities into a single executable. Most people will create a
link to busybox for each function they wish to use and BusyBox
will act like whatever it was invoked as.
Currently defined functions:
[, [[, acpid, addgroup, adduser, adjtimex, ar, arp, arping, ash, awk, base64, basename, bb, bbconfig, bbsh, blkid, blockdev, brctl, bunzip2, bzcat, bzip2, cal, cat, catv, chat, chattr, chgrp, chmod, chown, chpasswd, chpst,
chroot, chrt, chvt, cksum, clear, cmp, comm, conspy, cp, cpio, crond, cryptpw, cttyhack, cut, date, dd, deallocvt, delgroup, deluser, depmod, devmem, df, dhcprelay, diff, dirname, dmesg, dnsdomainname, dos2unix, du, dumpkmap,
dumpleases, echo, ed, egrep, eject, env, envdir, envuidgid, ether-wake, expand, expr, false, fbset, fdflush, fdformat, fdisk, fgconsole, fgrep, find, findfs, flash_eraseall, flash_lock, flash_unlock, flashcp, flock, free,
freeramdisk, fsck, fstrim, fsync, ftpd, fuser, getopt, getty, ginit, grep, groups, gunzip, gzip, halt, hd, hdparm, head, hexdump, hostname, httpd, hwclock, id, ifconfig, ifdown, ifenslave, ifplugd, ifup, init, insmod, install,
ionice, iostat, ip, ipaddr, ipcrm, ipcs, iplink, iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5, last, less, linux32, linux64, linuxrc, ln, loadfont, loadkmap, login, losetup, lpq, lpr, ls, lsattr, lsmod, lsof,
lspci, lsusb, lzcat, lzma, lzop, lzopcat, makedevs, man, md5sum, mdev, mesg, microcom, mkdir, mkdosfs, mke2fs, mkfifo, mkfs.ext2, mkfs.reiser, mkfs.vfat, mknod, mkpasswd, mkswap, mktemp, modinfo, modprobe, more, mount,
mountpoint, mpstat, mt, mv, nameif, nanddump, nandwrite, nbd-client, nc, netstat, nice, nmeter, nohup, nslookup, ntpd, openvt, passwd, patch, pgrep, pidof, ping, ping6, pipe_progress, pivot_root, pkill, pmap, popmaildir,
poweroff, powertop, printenv, printf, ps, pscan, pstree, pwd, pwdx, raidautorun, rdate, readahead, readlink, realpath, reboot, renice, reset, resize, rev, rm, rmdir, rmmod, route, rtcwake, runlevel, rx, script, scriptreplay,
sed, sendmail, seq, setarch, setconsole, setfont, setkeycodes, setlogcons, setserial, setsid, setuidgid, sh, sha1sum, sha256sum, sha3sum, sha512sum, showkey, sleep, softlimit, sort, split, start-stop-daemon, stat, strings, stty,
su, sum, swapoff, swapon, switch_root, sync, sysctl, tac, tail, tar, tee, telnet, telnetd, test, tftp, tftpd, time, timeout, top, touch, tr, traceroute, traceroute6, true, tty, ttysize, tunctl, tune2fs, ubiattach, ubidetach,
ubimkvol, ubirmvol, ubirsvol, ubiupdatevol, udhcpc, udhcpc6, udhcpd, umount, uname, uncompress, unexpand, uniq, unix2dos, unlzma, unlzop, unxz, unzip, uptime, users, usleep, vconfig, vi, vlock, volname, wall, watch, watchdog,
wc, wget, which, who, whoami, whois, xargs, xz, xzcat, yes, zcat, zcip
因此,我不明白多架构系统如何为正确的架构使用正确的解释器库,因为它不起作用,而且我的理解还停留在这一点上。
答案1
在 Linux 中,您只能运行与内核具有相同字节序的二进制文件。如果您想运行 armeb(大端 ARM)二进制文件,则需要有 armeb 内核;如果您想运行 armel 二进制文件,则需要有 armel 内核。对于 Linux 中支持大端和小端模式的所有 CPU 架构(ARM、ARM64、ARC、C6x、PowerPC、MIPS、m32r、microblaze、SH4、xtensa),情况都是如此。
有两种方法可以解决这个问题,但都存在明显的缺点:
你可以运行任何二进制使用qemu 用户模拟。一个典型的例子是在 x86 上运行 ARM 二进制文件,但您也可以在大端 ARM 内核或任何其他组合上运行 PowerPC 小端二进制文件,只要主机和目标都受 qemu 支持。由于 qemu 中缺少模拟代码,某些东西(例如罕见的设备驱动程序)可能无法工作,并且通常一切都会运行缓慢,通常比执行本机指令慢 10 倍。
只要您有硬件支持,就可以以与主机不同的字节序运行 KVM 客户机。这适用于某些 ARM32(Cortex-A7、A12、A15、A17,但不包括 Cortex-A9 或更早版本)以及 PowerPC 和 ARM64。客户机的进程对主机不可见,反之亦然,您必须设置内部网络和存储,以便客户机执行任何有用的操作。
答案2
好吧,看起来这是一个简单的硬编码路径问题。
localhost# readelf -l /bin/zsh5
Elf file type is EXEC (Executable file)
Entry point 0x40f015
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
在此路径上添加此库并ld-2.19.so
解决LD_LIBRARY_PATH
了我的问题。
因此它可能是第一个能够以与运行 i686 相同的方式运行 amd64 和 mips 的系统:)。