Scripts for creating Ada/Algol68/etc CROSS-COMPILER for AARCH64 - Minimal Dependencies

I hope this may be helpful to others. Please accept my apologies its lengthiness.

This post is for anyone interested in building an up-to-date gcc cross-compiler for aarch64 without having to wait for their linux distro. It been verified to produce running executables in 4 languages on a Raspberry Pi running Linux.

Below are a collection of small bash scripts to be run sequentially.

These build a gcc cross-compiler for aarch64 for the languages (ada, algol68, fortran, c, c++).

The approach aims for minimal dependencies, building everything from source. This also will compile glibc, and thus requires a couple of extra (but painless steps).

The first file is not a bash script. It needs to be “sourced” (since we need to propagate the environment variables we define herein. There are 3 env. variables you need to set to match your file placement desires: $PREFIX, $WORK, and $FETCHED_FILES_DIR. And three more to set to define versions to build (GCC, GlibC, and Binutils).

step_0_env_variables_and_install_directory

# This must be run using "source".
echo "step_0_env_variables_and_install_directory"

# Set PREFIX - the directory in which compiled software shall reside
export PREFIX="${HOME}/my_cross_compiler"

# WORK in a temporary location for the working files used during compilation
export WORK="${HOME}/Build_Tmp_Files/my_gcc_build"

#  FETCHED_FILES_DIR is a place to store fetched files, can copy files from
#  here to avoid multiple downloads
export FETCHED_FILES_DIR="${HOME}/My_Fetched_Files"

# Set versions of GCC, GLIBC, and Binutils desired
export GCC_VERSION_TO_BUILD="16.1.0"
export GLIBC_VERSION_TO_BUILD="2.43"
export BINUTILS_VERSION="2.46.1"

# Create installation directory for the compiled compiler,
if [ -d "$PREFIX" ]; then
   echo "Installation directory: "${PREFIX}" already exists, terminating."
   echo "Either use a different PREFIX, or remove this directory and try again."
   return
fi

echo "Creating installation directory: "${PREFIX}" ..."
mkdir -p $PREFIX
export PATH=${PREFIX}"/bin:$PATH"
echo ${PREFIX}" has been added to PATH"
echo "done."

# Create clean working directory for use during compilation
if [ -d "$WORK" ]; then
   echo "The temporary work directory: "${WORK}" already exists."

   read -p "Shall this be nuked? (yes/no) ? " choice
   case "$choice" in
      yes ) echo "nuking ${WORK} in 3 seconds and then will be remade"; \
            sleep 3; rm -Rf ${WORK} ;;
      *   ) echo "nothing deleted.";;
   esac
fi
echo "Creating temporary work directory: "${WORK}" ..."
mkdir -p $WORK
echo "done."

echo "Creating fetched files directory: "${FETCHED_FILES_DIR}" ..."
mkdir -p $FETCHED_FILES_DIR
echo "done."

# Define temporary working directory for downloads and compilation.
export BINUTILS_FILE="binutils-"${BINUTILS_VERSION}".tar.bz2"
export GCC_FILE="gcc-"${GCC_VERSION_TO_BUILD}".tar.gz"
export GLIBC_FILE="glibc-"${GLIBC_VERSION_TO_BUILD}".tar.bz2"
echo "Defined version and filenames, created directories."

Next is to download and extract the source files needed
step_1a_download_and_extract_files.sh

#!/bin/bash
echo "step_1a_download_and_extract_files.sh"

# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo "Work directory: "${WORK}" does not exist, exiting."
   exit 1
fi
cd $WORK

if [ -f ${WORK}/${BINUTILS_FILE} ]; then
   echo ${BINUTILS_FILE} " already exists, not fetching."
else
   echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."
   echo " attempting fetch of: " ${BINUTILS_FILE}
   wget https://ftpmirror.gnu.org/binutils/${BINUTILS_FILE}
   echo " fetched."
   tar xf ${BINUTILS_FILE}
   echo " extracted."
   mv $BINUTILS_FILE $FETCHED_FILES_DIR
   echo " moved to: ${FETCHED_FILES_DIR}"
   echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."
   echo
fi

if [ -f ${WORK}/${GCC_FILE} ]; then
   echo ${GCC_FILE} " already exists, not fetching."
else
   echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."
   echo " attempting fetch of: " ${GCC_FILE}
   wget https://ftpmirror.gnu.org/gcc/gcc-${GCC_VERSION_TO_BUILD}/${GCC_FILE}
   echo " fetched."
   tar xf ${GCC_FILE}
   echo " extracted."
   mv $GCC_FILE $FETCHED_FILES_DIR
   echo " moved to: ${FETCHED_FILES_DIR}"
   echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."
   echo 
fi

if [ -f ${WORK}/${GLIBC_FILE} ]; then
   echo ${GLIBC_FILE} " already exists, not fetching."
else
   echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."
   echo " attempting fetch of: " ${GLIBC_FILE}
   wget https://ftpmirror.gnu.org/glibc/${GLIBC_FILE}
   echo " fetched."
   tar xf ${GLIBC_FILE}
   echo " extracted."
   mv $GLIBC_FILE $FETCHED_FILES_DIR
   echo " moved to: ${FETCHED_FILES_DIR}"
   echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."
fi

git clone --depth=1 https://github.com/raspberrypi/linux
   echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."
   echo "                     raspberry pi linux cloned"
   cp -R linux $FETCHED_FILES_DIR
   echo " copied linux to: ${FETCHED_FILES_DIR}"
   echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."

Once the files have been downloaded, you could use this next step as alternative to re-downloading if you ever need to re-run any build steps. (For example, if you decide to “start-over” from the beginning.)

step_1b_copy_local_files_and_extract_files.sh

#!/bin/bash
echo "step_1b_copy_local_files_and_extract_files.sh"
# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo "Work directory: "${WORK}" does not exist, exiting."
   exit 1
fi

cd $WORK

echo "extracting fetched files."
tar xf $FETCHED_FILES_DIR/$BINUTILS_FILE
tar xf $FETCHED_FILES_DIR/$GCC_FILE
tar xf $FETCHED_FILES_DIR/$GLIBC_FILE

echo "copying linux from: $FETCHED_FILES_DIR"
cp -r $FETCHED_FILES_DIR/linux .

echo "done."

step_2_build_binutils.sh

#!/bin/bash
echo "step_2_build_binutils.sh"
# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo "Work directory: "${WORK}" does not exist, exiting."
   exit 1
fi

cd $WORK

mkdir build-binutils
cd build-binutils

echo ${BINUITLS_VERSION}

../"binutils-"${BINUTILS_VERSION}/configure \
   --prefix=$PREFIX \
   --target=aarch64-linux-gnu \
   --disable-multilib

make -j 4
make install

step_3_gcc_prerequisites.sh

#!/bin/bash
echo "step_3_gcc_prerequisites.sh"
# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo "Work directory: "${WORK}" does not exist, exiting."
   exit 1
fi

cd $WORK

echo "Downloading GCC prerequisties ... "

cd "gcc-"${GCC_VERSION_TO_BUILD}
contrib/download_prerequisites
rm *.tar.*

cd $WORK

echo " ... done. "

step_4_kernel_headers.sh

#!/bin/bash
echo "step_4_kernel_headers.sh"
# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo "Work directory: "${WORK}" does not exist, exiting."
   exit 1
fi

cd $WORK
cd linux

KERNEL=kernel7

make ARCH=arm64 INSTALL_HDR_PATH=$PREFIX/aarch64-linux-gnu headers_install

step_5_partial_gcc-16.1_build_SEE_NOTE.sh

#!/bin/bash
echo "step_5_partial_gcc-16.1_build_SEE_NOTE.sh"
# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo "Work directory: "${WORK}" does not exist, exiting."
   exit 1
fi
cd $WORK

echo "Configuring GCC ... "

mkdir build-gcc
cd build-gcc

../"gcc-"${GCC_VERSION_TO_BUILD}/configure \
   --prefix=$PREFIX \
   --target=aarch64-linux-gnu \
   --enable-languages=c,c++,ada,algol68,fortran \
   --enable-libada \
   --with-arch=armv8-a \
   --disable-multilib

make -j4 all-gcc
make install-gcc

echo
echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."
echo " NOTE: step_9_install_libada.sh (and others) is needed, because trying to"
echo " do 'make all-target-libada' at this stage would cause a compilation"
echo " complaint about pthread."
echo ".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."
echo

step_6_partial_Glibc_build.sh

#!/bin/bash
echo "step_6_partial_Glibc_build.sh"
# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo Work directory: $WORK does not exist, exiting.
   exit 1
fi
cd $WORK

echo "Configuring GLIBC ... "

mkdir build-glibc 
cd build-glibc

 ../glibc-2.43/configure \
    --prefix=$PREFIX/aarch64-linux-gnu \
    --build=$MACHTYPE \
    --host=aarch64-linux-gnu \
    --target=aarch64-linux-gnu \
    --with-arch=armv8-a \
    --with-headers=$PREFIX/aarch64-linux-gnu/include \
    --disable-multilib libc_cv_forced_unwind=yes

make install-bootstrap-headers=yes install-headers
make -j4 csu/subdir_lib

install csu/crt1.o csu/crti.o csu/crtn.o $PREFIX/aarch64-linux-gnu/lib

aarch64-linux-gnu-gcc \
   -nostdlib -nostartfiles -shared -x c /dev/null \
   -o $PREFIX/aarch64-linux-gnu/lib/libc.so

touch $PREFIX/aarch64-linux-gnu/include/gnu/stubs.h

step_7_back_to_gcc.sh

#!/bin/bash
echo "step_7_back_to_gcc.sh"
# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo "Work directory: "${WORK}" does not exist, exiting."
   exit 1
fi

cd $WORK
cd build-gcc

make -j4 all-target-libgcc
make -j4 -C gcc cross-gnattools ada.all.cross
make -j4 install-strip-gcc install-target-libgcc

step_8_finish_building_Glibc_with_hack_fix.sh

#!/bin/bash
echo "step_8_finish_building_Glibc_with_hack_fix.sh"
# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo "Work directory: "${WORK}" does not exist, exiting."
   exit 1
fi

cd $WORK

# Step 8 would fail with error about "cannot find libatomic_asneeded".
# A quick hack solution avoids this by touching the expected file:
#
touch $PREFIX/aarch64-linux-gnu/lib/libatomic_asneeded.a

cd build-glibc
make -j1
make install

echo
echo ".  .  .  .  .  .  .  .  .  .  .  .  .  . . .  .  .  .  .  .  .  .  .  ."
echo "To avoid a compilation error, a fix hack has been performed here, namely:"
echo "touch $PREFIX/aarch64-linux-gnu/lib/libatomic_asneeded.a"
echo
echo "Everything is probably just fine."
echo ".  .  .  .  .  .  .  .  .  .  .  .  .  . . .  .  .  .  .  .  .  .  .  ."

step_9_install_libada_libga68_libgfortran.sh

#!/bin/bash
echo "step_9_install_libada_libga68_libgfortran.sh"
# exit script on any error
set -e

if [ ! -d "$WORK" ]; then
   echo "Work directory: "${WORK}" does not exist, exiting."
   exit 1
fi
cd $WORK

echo "Installing libada, libga68, libgfortran ... "

cd build-gcc

make all-target-libada 
make install-target-libada

make all-target-libga68
make install-target-libga68

make all-target-libgfortran
make install-target-libgfortran

echo "installed: libada, libga68, libgfortran."

If all went well, we can do a quick verification now.

step_10_verify_it_works.sh

#!/bin/bash
echo "step_10_verify_it_works.sh"

echo "The executables produced should work on a Raspberry Pi running linux."

echo "Compiling aarch64 c program"
$PREFIX/bin/aarch64-linux-gnu-gcc -o hello_from_c hello_from_c.c

echo "Compiling aarch64 ada program"
$PREFIX/bin/aarch64-linux-gnu-gnatmake hello_from_ada.adb

echo "Compiling aarch64 algol68 program"
$PREFIX/bin/aarch64-linux-gnu-ga68 -o hello_from_algol68 hello_from_algol68.a68

echo "Compiling aarch64 fortran program"
$PREFIX/bin/aarch64-linux-gnu-gfortran -static -o hello_from_fortran hello_from_fortran.f90

Which uses source files:

hello_from_c.c

#include <stdio.h>

int main() {
   printf("Hello, from C!\n");
   return 0;
}

hello_from_ada.adb

with Ada.Text_IO; use Ada.Text_IO;

procedure hello_from_ada is
begin
  Put_Line("Hello from Ada!");
end hello_from_ada;

hello_from_algol68.a68

begin
  puts("Hello from Algol68!'n")
end

hello_from_fortran.f90

program hello_from_fortran
   print *, 'Hello from Fortran!'
end program hello_from_fortran
1 Like