Decades ago, cross-compilers were the only way to build software for a different architecture. Today, we have qemu-user-static and Buildah to build software for a different architecture on the same machine. This article will show you how to use Buildah and qemu-user-static to build software for a different architecture on Fedora.

If you are not farmiliar with buildah and qemu-user-static, please have a look at the project pages: Buildah and Qemu User space emulator.

Install buildah

sudo dnf install -y buildah

Install qemu-user-static

sudo dnf install -y qemu-user-static-aarch64 qemu-user qemu-user-binfmt

In the above command we are installing qemu-user-static for aarch64 architecture. If you want to build for a different architecture, please replace aarch64 with the desired architecture. The qemu-user and qemu-user-binfmt packages are not needed for buildah to work, but they are needed if you want to run the binaries on your machine.

Compile a simple C program for aarch64

export hello_c=$(cat <<EOF
#include <stdio.h>
int main() {
// printf() displays the string inside quotation
printf(\"Hello, World!\");
return 0;
}
EOF
)

export container=$(buildah from arm64v8/alpine:3.18)
buildah config --label maintainer=""github.com/deamen"" $container
buildah run $container apk add git gcc make libc-dev
buildah run $container mkdir /test
buildah config --workingdir "/test" $container
buildah run $container sh -c "echo \"$hello_c\" > hello.c"

buildah run $container gcc -o /test/hello /test/hello.c

copy_script="copy_artifact.sh"
cat << 'EOF' >> $copy_script
#!/bin/sh
mnt=$(buildah mount $container)
cp $mnt/test/hello ./hello
buildah umount $container
EOF
chmod a+x $copy_script
buildah unshare ./$copy_script
rm ./$copy_script

buildah rm $container

When you run the script, a file called hello will be created in the current directory. This file is a binary for aarch64 architecture. You can verrify this by running file hello command. The output should be something like this:

$ file hello
hello: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped

Note: You may see a warning message: WARNING: image platform (linux/arm64/v8) does not match the expected platform (linux/amd64). This can be safely ignored as it is what we are trying to achieve.

Copy artifacts from the container

The above script uses a shell script to copy the binary from the container to the host. This is because buildah does not have a command to copy files from the container to the host. The script mounts the container and copies the binary to the host. This is not the best way to copy files from the container to the host, but it is the only way I know of. If you know a better way, please let me know.

The code snippet below needs to be added to the end of the buildah script to copy the binary from the container to the host.

copy_script="copy_artifact.sh"
cat << 'EOF' >> $copy_script
#!/bin/sh
mnt=$(buildah mount $container)
cp $mnt/test/hello ./hello
buildah umount $container
EOF
chmod a+x $copy_script
buildah unshare ./$copy_script
rm ./$copy_script