void-mklive/lib.sh.in
Cameron Nemo 35209b255c lib: install qemu binfmts less often
This change ensures that 32 bit architecture targets are created by
capable hosts without qemu.

Installing qemu binfmts was unnecessary for some targets and causing
issues.

Closes #168
2021-02-19 21:07:27 -08:00

338 lines
13 KiB
Bash

#!/bin/sh
# This contains the COMPLETE list of binaries that this script needs
# to function. The only exception is the QEMU binary since it is not
# known in advance which one wil be required.
readonly LIBTOOLS="cp echo cat printf which mountpoint mount umount modprobe"
readonly HOSTARCH=$(xbps-uhelper arch)
info_msg() {
# This function handles the printing that is bold within all
# scripts. This is a convenience function so that the rather ugly
# looking ASCII escape codes live in only one place.
printf "\033[1m%s\n\033[m" "$@"
}
die() {
# This function is registered in all the scripts to make sure that
# the important mounts get cleaned up and the $ROOTFS location is
# removed.
printf "FATAL: %s\n" "$@"
umount_pseudofs
[ -d "$ROOTFS" ] && rm -rf "$ROOTFS"
exit 1
}
check_tools() {
# All scripts within mklive declare the tools they will use in a
# variable called "REQTOOLS". This function checks that these
# tools are available and prints out the path to each tool that
# will be used. This can be useful to figure out what is broken
# if a different version of something is used than was expected.
for tool in $LIBTOOLS $REQTOOLS ; do
if ! which "$tool" > /dev/null ; then
die "Required tool $tool is not available on this system!"
fi
done
info_msg "The following tools will be used:"
for tool in $LIBTOOLS $REQTOOLS ; do
which "$tool"
done
}
mount_pseudofs() {
# This function ensures that the psuedofs mountpoints are present
# in the chroot. Strictly they are not necessary to have for many
# commands, but bind-mounts are cheap and it isn't too bad to just
# mount them all the time.
for f in dev proc sys; do
# In a naked chroot there is nothing to bind the mounts to, so
# we need to create directories for these first.
[ ! -d "$ROOTFS/$f" ] && mkdir -p "$ROOTFS/$f"
if ! mountpoint -q "$ROOTFS/$f" ; then
# It is VERY important that this only happen if the
# pseudofs isn't already mounted. If it already is then
# this is virtually impossible to troubleshoot because it
# looks like the subsequent umount just isn't working.
mount -r --bind /$f "$ROOTFS/$f"
fi
done
if ! mountpoint -q "$ROOTFS/tmp" ; then
mkdir -p "$ROOTFS/tmp"
mount -o mode=0755,nosuid,nodev -t tmpfs tmpfs "$ROOTFS/tmp"
fi
}
umount_pseudofs() {
# This function cleans up the mounts in the chroot. Failure to
# clean up these mounts will prevent the tmpdir from being
# deletable instead throwing the error "Device or Resource Busy".
# The '-f' option is passed to umount to account for the
# contingency where the psuedofs mounts are not present.
if [ -d "${ROOTFS}" ]; then
for f in dev proc sys; do
umount -f "$ROOTFS/$f" >/dev/null 2>&1
done
fi
umount -f "$ROOTFS/tmp" >/dev/null 2>&1
}
run_cmd_target() {
info_msg "Running $* for target $XBPS_TARGET_ARCH ..."
if [ "$XBPS_TARGET_ARCH" = "${HOSTARCH}" ] ||
[ -z "${XBPS_TARGET_ARCH##*86*}" ] &&
[ -z "${HOSTARCH##*86*}" ] ; then
# This is being run on the same architecture as the host,
# therefore we should set XBPS_ARCH.
if ! eval XBPS_ARCH="$XBPS_TARGET_ARCH" "$@" ; then
die "Could not run command $*"
fi
else
# This is being run on a foriegn arch, therefore we should set
# XBPS_TARGET_ARCH. In this case XBPS will not attempt
# certain actions and will require reconfiguration later.
if ! eval XBPS_TARGET_ARCH="$XBPS_TARGET_ARCH" "$@" ; then
die "Could not run command $*"
fi
fi
}
run_cmd() {
# This is a general purpose function to run commands that a user
# may wish to see. For example its useful to see the tar/xz
# pipeline to not need to delve into the scripts to see what
# options its set up with.
info_msg "Running $*"
eval "$@"
}
run_cmd_chroot() {
# General purpose chroot function which makes sure the chroot is
# prepared. This function takes 2 arguments, the location to
# chroot to and the command to run.
# This is an idempotent function, it is safe to call every time
# before entering the chroot. This has the advantage of making
# execution in the chroot appear as though it "Just Works(tm)".
register_binfmt
# Before we step into the chroot we need to make sure the
# pseudo-filesystems are ready to go. Not all commands will need
# this, but its still a good idea to call it here anyway.
mount_pseudofs
# With assurance that things will run now we can jump into the
# chroot and run stuff!
chroot "$1" sh -c "$2"
}
cleanup_chroot() {
# This function cleans up the chroot shims that are used by QEMU
# to allow builds on alien platforms. It takes no arguments but
# expects the global $ROOTFS variable to be set.
# Un-Mount the pseudofs mounts if they were mounted
umount_pseudofs
# If a QEMU binary was copied in, remove that as well
if [ -n "$QEMU_BIN" ] && [ -x "$ROOTFS/usr/bin/$QEMU_BIN" ] ; then
rm "$ROOTFS/usr/bin/$QEMU_BIN"
fi
}
register_binfmt() {
# This function sets up everything that is needed to be able to
# chroot into a ROOTFS and be able to run commands there. This
# really matters on platforms where the host architecture is
# different from the target, and you wouldn't be able to run
# things like xbps-reconfigure -a. This function is idempotent
# (You can run it multiple times without modifying state). This
# function takes no arguments, but does expect the global variable
# $XBPS_TARGET_ARCH to be set.
# This select sets up the "magic" bytes in /proc that let the
# kernel select an alternate interpreter. More values for this
# map can be obtained from here:
# https://github.com/qemu/qemu/blob/master/scripts/qemu-binfmt-conf.sh
# If the XBPS_TARGET_ARCH is unset but the PLATFORM is known, it
# may be possible to set the architecture from the static
# platforms map.
if [ -z "$XBPS_TARGET_ARCH" ] && [ ! -z "$PLATFORM" ] ; then
set_target_arch_from_platform
fi
# In the special case where the build is native we can return
# without doing anything else
# This is only a basic check for identical archs, with more careful
# checks below for cases like ppc64 -> ppc and x86_64 -> i686.
_hostarch="${HOSTARCH%-musl}"
_targetarch="${XBPS_TARGET_ARCH%-musl}"
if [ "$_hostarch" = "$_targetarch" ] ; then
return
fi
case "${_targetarch}" in
armv*)
# TODO: detect aarch64 hosts that run 32 bit ARM without qemu (some cannot)
if ( [ "${_targetarch}" = "armv6l" ] && [ "${_hostarch}" = "armv7l" ] ) ; then
return
fi
if [ "${_targetarch}" = "armv5tel" -a \
\( "${_hostarch}" = "armv6l" -o "${_hostarch}" = "armv7l" \) ] ; then
return
fi
_cpu=arm
_magic="\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00"
_mask="\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"
QEMU_BIN=qemu-arm-static
;;
aarch64)
_cpu=aarch64
_magic="\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7"
_mask="\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"
QEMU_BIN=qemu-aarch64-static
;;
ppc64le)
_cpu=ppc64le
_magic="\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15\x00"
_mask="\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\x00"
QEMU_BIN=qemu-ppc64le-static
;;
ppc64)
_cpu=ppc64
_magic="\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15"
_mask="\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"
QEMU_BIN=qemu-ppc64-static
;;
ppc)
if [ "$_hostarch" = "ppc64" ] ; then
return
fi
_cpu=ppc
_magic="\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14"
_mask="\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"
QEMU_BIN=qemu-ppc-static
;;
mipsel)
if [ "$_hostarch" = "mips64el" ] ; then
return
fi
_cpu=mipsel
_magic="\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00"
_mask="\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"
QEMU_BIN=qemu-mipsel-static
;;
x86_64)
_cpu=x86_64
_magic="\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00"
_mask="\xff\xff\xff\xff\xff\xfe\xfe\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"
QEMU_BIN=qemu-x86_64-static
;;
i686)
if [ "$_hostarch" = "x86_64" ] ; then
return
fi
_cpu=i386
_magic="\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00"
_mask="\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"
QEMU_BIN=qemu-i386-static
;;
*)
die "Unknown target architecture!"
;;
esac
# For builds that do not match the host architecture, the correct
# qemu binary will be required.
if ! $QEMU_BIN -version >/dev/null 2>&1; then
die "$QEMU_BIN binary is missing in your system, exiting."
fi
# In order to use the binfmt system the binfmt_misc mountpoint
# must exist inside of proc
if ! mountpoint -q /proc/sys/fs/binfmt_misc ; then
modprobe -q binfmt_misc
mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc 2>/dev/null
fi
# Only register if the map is incomplete
if [ ! -f /proc/sys/fs/binfmt_misc/qemu-$_cpu ] ; then
echo ":qemu-$_cpu:M::$_magic:$_mask:/usr/bin/$QEMU_BIN:" > /proc/sys/fs/binfmt_misc/register 2>/dev/null
fi
# If the static binary isn't in the chroot then the chroot will
# fail. The kernel knows about the map but without the static
# version there's no interpreter in the chroot, only the
# dynamically linked one in the host. To simplify things we just
# use the static one always and make sure it shows up at the same
# place in the host and the chroot.
if [ ! -x "$ROOTFS/usr/bin/$QEMU_BIN" ] ; then
install -m755 -D "$(which "$QEMU_BIN")" "$ROOTFS/usr/bin/$QEMU_BIN" ||
die "Could not install $QEMU_BIN to $ROOTFS/usr/bin/"
fi
}
set_target_arch_from_platform() {
# This function maintains a lookup from platform to target
# architecture. This is required for scripts that need to know
# the target architecture, but don't necessarily need to know it
# internally (i.e. only run_cmd_chroot).
case "$PLATFORM" in
bananapi*) XBPS_TARGET_ARCH="armv7l";;
beaglebone*) XBPS_TARGET_ARCH="armv7l";;
cubieboard2*|cubietruck*) XBPS_TARGET_ARCH="armv7l";;
odroid-u2*) XBPS_TARGET_ARCH="armv7l";;
odroid-c2*) XBPS_TARGET_ARCH="aarch64";;
rpi3*) XBPS_TARGET_ARCH="aarch64";;
rpi2*) XBPS_TARGET_ARCH="armv7l";;
rpi*) XBPS_TARGET_ARCH="armv6l";;
ci20*) XBPS_TARGET_ARCH="mipsel";;
i686*) XBPS_TARGET_ARCH="i686";;
x86_64*) XBPS_TARGET_ARCH="x86_64";;
GCP*) XBPS_TARGET_ARCH="x86_64";;
pinebookpro*) XBPS_TARGET_ARCH="aarch64";;
*) die "$PROGNAME: Unable to compute target architecture from platform";;
esac
if [ -z "${PLATFORM##*-musl}" ] ; then
XBPS_TARGET_ARCH="${XBPS_TARGET_ARCH}-musl"
fi
}
set_dracut_args_from_platform() {
# In rare cases it is necessary to set platform specific dracut
# args. This is mostly the case on ARM platforms.
case "$PLATFORM" in
*) ;;
esac
}
set_cachedir() {
# The package artifacts are cacheable, but they need to be isolated
# from the host cache.
: "${XBPS_CACHEDIR:=--cachedir=$PWD/xbps-cache/${XBPS_TARGET_ARCH}}"
}
# These should all resolve even if they won't have the appropriate
# repodata files for the selected architecture.
: "${XBPS_REPOSITORY:=--repository=http://alpha.de.repo.voidlinux.org/current \
--repository=http://alpha.de.repo.voidlinux.org/current/musl \
--repository=http://alpha.de.repo.voidlinux.org/current/aarch64}"
# This library is the authoritative source of the platform map,
# because of this we may need to get this information from the command
# line. This select allows us to get that information out. This
# fails silently if the toolname isn't known since this script is
# sourced.
case $1 in
platform2arch)
PLATFORM=$2
set_target_arch_from_platform
echo "$XBPS_TARGET_ARCH"
;;
esac