#!/usr/bin/env bash # # SPDX-License-Identifier: GPL-2.0-only # Usage: [--debug] [path to xgcc/bin directory] # Enable debug output if [ "$1" = "--debug" ]; then shift set -x fi # GENERIC_COMPILER_PREFIX defaults to empty but can be used to override # compiler search behavior TMPFILE="" XGCCPATH=$1 # payloads under payloads/external crossgcc path if [ -d "$(pwd)/../../../../util/crossgcc/xgcc/bin/" ] then XGCCPATH=${XGCCPATH:-"$(pwd)/../../../../util/crossgcc/xgcc/bin/"} fi # libpayload crossgcc path if [ -d "$(pwd)/../../util/crossgcc/xgcc/bin/" ] then XGCCPATH=${XGCCPATH:-"$(pwd)/../../util/crossgcc/xgcc/bin/"} fi # coreboot crossgcc path if [ -d "$(pwd)/util/crossgcc/xgcc/bin/" ] then XGCCPATH=${XGCCPATH:-"$(pwd)/util/crossgcc/xgcc/bin/"} fi die() { echo "ERROR: $*" >&2 exit 1 } clean_up() { if [ -n "$TMPFILE" ]; then rm -f "$TMPFILE" "$TMPFILE.c" "$TMPFILE.o" fi } trap clean_up EXIT program_exists() { type "$1" >/dev/null 2>&1 } if [ "$("${XGCCPATH}/iasl" 2>/dev/null | grep -c ACPI)" -gt 0 ]; then IASL=${XGCCPATH}/iasl elif [ "$(iasl 2>/dev/null | grep -c ACPI)" -gt 0 ]; then IASL=iasl fi if program_exists "${XGCCPATH}/nasm" ; then NASM="${XGCCPATH}/nasm" elif program_exists nasm; then NASM=nasm fi if program_exists "${XGCCPATH}/gcc"; then HOSTCC="${XGCCPATH}/gcc" elif program_exists gcc; then HOSTCC=gcc elif program_exists cc; then HOSTCC=cc else die "no host compiler found" fi # Look for a C++ compiler (for kconfig's qconf), but don't fail if there is # none, just set the compiler to false(1) which will break early enough if # used while being less confusing than errors about "g not found" when # "$HOSTCXX -g" evaluates to "-g" and make drops the leading dash. if program_exists "${XGCCPATH}/g++"; then HOSTCXX="${XGCCPATH}/g++" elif program_exists g++; then HOSTCXX=g++ elif program_exists c++; then HOSTCXX=c++ else HOSTCXX=false fi # try to find the core count using various methods CORES="$(getconf _NPROCESSORS_ONLN 2>/dev/null)" if [ -z "$CORES" ]; then NPROC=$(command -v nproc) if [ -n "$NPROC" ]; then CORES="$($NPROC)" fi fi if [ -z "$CORES" ]; then SYSCTL=$(command -v sysctl) if [ -n "$SYSCTL" ]; then CORES="$(${SYSCTL} -n hw.ncpu 2>/dev/null)" fi fi if [ -z "$CORES" ] && [ -f /proc/cpuinfo ]; then CORES="$(grep 'processor' /proc/cpuinfo 2>/dev/null | wc -l)" fi cat <"$tmp_c" "$cc" -nostdlib -Werror $cflags -c "$tmp_c" -o "$tmp_o" >/dev/null 2>&1 } testld() { local gcc="$1" local cflags="$2" local ld="$3" local ldflags="$4" local tmp_o="$TMPFILE.o" local tmp_elf="$TMPFILE.elf" rm -f "$tmp_elf" testcc "$gcc" "$cflags" && $ld -nostdlib -static $ldflags -o "$tmp_elf" "$tmp_o" >/dev/null 2>&1 } testas() { local gccprefix="$1" local twidth="$2" local arch="$3" local use_dash_twidth="$4" local endian="$5" local obj_file="$TMPFILE.o" local full_arch="elf$twidth-$arch" rm -f "$obj_file" [ -n "$use_dash_twidth" ] && use_dash_twidth="--$twidth" [ -n "$endian" ] && endian="-$endian" "${gccprefix}as" $use_dash_twidth $endian -o "$obj_file" "$TMPFILE" \ 2>/dev/null || return 1 # Check output content type. local obj_type="$(LANG=C LC_ALL='' "${gccprefix}"objdump -p "$obj_file" 2>/dev/null)" local obj_arch="$(expr "$obj_type" : '.*format \(.[a-z0-9-]*\)')" [ "$obj_arch" = "$full_arch" ] || return 1 unset ASFLAGS LDFLAGS unset FLAGS_GCC CFLAGS_GCC CFLAGS_CLANG if [ -n "$use_dash_twidth" ]; then ASFLAGS="--$twidth" FLAGS_GCC="-m$twidth" CFLAGS_CLANG="-m$twidth" LDFLAGS="-b $full_arch" fi # Special parameters only available in dash_twidth mode. [ -n "$use_dash_twidth" ] && case "$full_arch" in "elf32-i386" ) LDFLAGS="$LDFLAGS -melf_i386" FLAGS_GCC="$FLAGS_GCC -Wl,-b,elf32-i386 -Wl,-melf_i386" CFLAGS_CLANG="$CFLAGS_CLANG -Wl,-b,elf32-i386 -Wl,-melf_i386" ;; esac return 0 } detect_special_flags() { local architecture="$1" # Check for an operational -m32/-m64 testcc "$GCC" "$FLAGS_GCC -m$TWIDTH " && FLAGS_GCC="$FLAGS_GCC -m$TWIDTH " # Use bfd linker instead of gold if available: testcc "$GCC" "$FLAGS_GCC -fuse-ld=bfd" && FLAGS_GCC="$FLAGS_GCC -fuse-ld=bfd" && LINKER_SUFFIX='.bfd' testcc "$GCC" "$FLAGS_GCC -fno-stack-protector" && FLAGS_GCC="$FLAGS_GCC -fno-stack-protector" testcc "$GCC" "$FLAGS_GCC -Wl,--build-id=none" && FLAGS_GCC="$FLAGS_GCC -Wl,--build-id=none" testcc "$GCC" "$CFLAGS_GCC -Wno-address-of-packed-member $FLAGS_GCC" && CFLAGS_GCC="$CFLAGS_GCC -Wno-address-of-packed-member" testcc "$GCC" "$CFLAGS_GCC --param=min-pagesize=1024 $FLAGS_GCC" && CFLAGS_GCC="$CFLAGS_GCC --param=min-pagesize=1024" case "$architecture" in x86) ;; x64) ;; arm64) testld "$GCC" "$FLAGS_GCC" "${GCCPREFIX}ld${LINKER_SUFFIX}" \ "$LDFLAGS --fix-cortex-a53-843419" && \ LDFLAGS_ARM64_A53_ERRATUM_843419+=" --fix-cortex-a53-843419" ;; riscv) testcc "$GCC" "$FLAGS_GCC -march=rv64iadc_zicsr_zifencei" && ARCH_SUFFIX="_zicsr_zifencei" ;; esac } detect_compiler_runtime() { test -z "$GCC" || \ CC_RT_GCC="$(${GCC} ${CFLAGS_GCC} ${FLAGS_GCC} -print-libgcc-file-name)" if [ ${CLANG_RUNTIME} = "libgcc" ]; then CC_RT_CLANG=${CC_RT_GCC} else test -z "$CLANG" || \ CC_RT_CLANG="$(${CLANG} ${CFLAGS_CLANG} -print-libgcc-file-name 2>/dev/null)" fi } report_arch_toolchain() { cat < /dev/null; then "arch_config_$architecture" else die "no architecture definition for $architecture" fi # To override toolchain, define CROSS_COMPILE_$arch or CROSS_COMPILE as # environment variable. # Ex: CROSS_COMPILE_arm="armv7a-cros-linux-gnueabi-" # CROSS_COMPILE_x86="i686-pc-linux-gnu-" search="$(eval echo "\$CROSS_COMPILE_$architecture" 2>/dev/null)" search="$search $CROSS_COMPILE" for toolchain in $TCLIST; do search="$search $XGCCPATH$toolchain-$TABI-" search="$search $toolchain-$TABI-" search="$search $toolchain-linux-gnu-" search="$search $toolchain-linux-" search="$search $toolchain-" search="$search $toolchain-linux-gnueabi-" done echo "###########################################################################" echo "# $architecture" echo "# TARCH_SEARCH=$search" # Search toolchain by checking assembler capability. for TBFDARCH in $TBFDARCHS; do for gccprefix in $search "$GENERIC_COMPILER_PREFIX"; do program_exists "${gccprefix}as" || continue for endian in $TENDIAN ""; do { testas "$gccprefix" "$TWIDTH" "$TBFDARCH" \ "" "$endian" || testas "$gccprefix" "$TWIDTH" "$TBFDARCH" \ "TRUE" "$endian" ; } && \ testcc "${gccprefix}gcc" "$CFLAGS_GCC" "$FLAGS_GCC" && \ GCCPREFIX="$gccprefix" && \ break 3 done done done if [ "invalid" != "$GCCPREFIX" ]; then GCC="${GCCPREFIX}gcc" fi for clang_arch in $TCLIST invalid; do for clang_prefix in $search $XGCCPATH "$GENERIC_COMPILER_PREFIX"; do testcc "${clang_prefix}clang" "-target ${clang_arch}-$TABI -c" && break 2 done done if [ "invalid" != "$clang_arch" ]; then # FIXME: this may break in a clang && !gcc configuration, # but that's more of a clang limitation. Let's be optimistic # that this will change in the future. CLANG="${clang_prefix}clang" CFLAGS_CLANG="-target ${clang_arch}-${TABI} $CFLAGS_CLANG" fi } OUT="$(mktemp /tmp/temp.XXXXXX 2>/dev/null || echo /tmp/temp.coreboot.$RANDOM)" rm -f $OUT for architecture in $SUPPORTED_ARCHITECTURES; do ( TMPFILE="$(mktemp /tmp/temp.XXXXXX 2>/dev/null || echo /tmp/temp.coreboot.$RANDOM)" touch $TMPFILE test_architecture "$architecture" detect_special_flags "$architecture" detect_compiler_runtime "$architecture" report_arch_toolchain clean_up ) > $OUT.$architecture & done wait for architecture in $SUPPORTED_ARCHITECTURES; do cat $OUT.$architecture rm -f $OUT.$architecture done echo XCOMPILE_COMPLETE:=1