| #!/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 <<EOF |
| # platform agnostic and host tools |
| XGCCPATH:=${XGCCPATH} |
| NASM:=${NASM} |
| IASL:=${IASL} |
| HOSTCC?=${HOSTCC} |
| HOSTCXX?=${HOSTCXX} |
| CPUS?=${CORES} |
| |
| EOF |
| |
| testcc() { |
| local cc="$1" |
| local cflags="$2" |
| local tmp_c="$TMPFILE.c" |
| local tmp_o="$TMPFILE.o" |
| rm -f "$tmp_c" "$tmp_o" |
| echo "void _start(void) {}" >"$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 <<EOF |
| # elf${TWIDTH}-${TBFDARCH} toolchain (${GCC}) |
| ARCH_SUPPORTED+=${TARCH} |
| SUBARCH_SUPPORTED+=${TSUPP-${TARCH}} |
| ARCH_SUFFIX_${TARCH}:=${ARCH_SUFFIX} |
| |
| # GCC |
| GCC_CC_${TARCH}:=${GCC} |
| GCC_CFLAGS_${TARCH}:=${CFLAGS_GCC} ${FLAGS_GCC} |
| # Generally available for GCC's cc1: |
| GCC_CFLAGS_${TARCH}+=-fno-delete-null-pointer-checks -Wlogical-op |
| GCC_ADAFLAGS_${TARCH}:=${FLAGS_GCC} |
| GCC_COMPILER_RT_${TARCH}:=${CC_RT_GCC} |
| GCC_COMPILER_RT_FLAGS_${TARCH}:=${CC_RT_EXTRA_GCC} |
| EOF |
| if [ "${TARCH}" = "x86_64" ]; then |
| cat <<EOF |
| GCC_CFLAGS_${TARCH} += -malign-data=abi |
| EOF |
| fi |
| cat <<EOF |
| |
| # Clang |
| CLANG_CC_${TARCH}:=${CLANG} |
| CLANG_CFLAGS_${TARCH}:=${CFLAGS_CLANG} |
| CLANG_CFLAGS_${TARCH}+=-Qunused-arguments -m${TWIDTH} |
| # tone down clang compiler warnings |
| CLANG_CFLAGS_${TARCH}+=-Wno-unused-variable -Wno-unused-function -Wno-tautological-compare |
| CLANG_CFLAGS_${TARCH}+=-Wno-shift-overflow -Wno-address-of-packed-member -Wno-initializer-overrides |
| CLANG_CFLAGS_${TARCH}+=-fbracket-depth=2048 -mllvm -asm-macro-max-nesting-depth=1000 |
| CLANG_COMPILER_RT_${TARCH}:=${CC_RT_CLANG} |
| CLANG_COMPILER_RT_FLAGS_${TARCH}:=${CC_RT_EXTRA_CLANG} |
| |
| # GCC/Clang Common |
| ifeq (\$(CONFIG_COMPILER_GCC)\$(CONFIG_LP_COMPILER_GCC),y) |
| CC_${TARCH}:=\$(GCC_CC_${TARCH}) |
| CFLAGS_${TARCH}:=\$(GCC_CFLAGS_${TARCH}) |
| COMPILER_RT_${TARCH}:=\$(GCC_COMPILER_RT_${TARCH}) |
| COMPILER_RT_FLAGS_${TARCH}:=\$(GCC_COMPILER_RT_FLAGS_${TARCH}) |
| else |
| CC_${TARCH}:=\$(CLANG_CC_${TARCH}) |
| CFLAGS_${TARCH}:=\$(CLANG_CFLAGS_${TARCH}) |
| COMPILER_RT_${TARCH}:=\$(CLANG_COMPILER_RT_${TARCH}) |
| COMPILER_RT_FLAGS_${TARCH}:=\$(CLANG_COMPILER_RT_FLAGS_${TARCH}) |
| endif |
| EOF |
| |
| # Generally the x86 should build for i686 -- no sse/mmx |
| # instructions since SMM modules are compiled using these |
| # flags. Note that this doesn't prevent a project using |
| # xcompile to explicitly specify -mmsse, etc flags. |
| # The Quark processor doesn't support the instructions |
| # introduced with the Pentium 6 architecture, so allow it |
| # to use i586 instead. |
| if [ "${TARCH}" = "x86_64" ]; then |
| cat <<EOF |
| CFLAGS_${TARCH} += -march=nocona |
| EOF |
| fi |
| |
| if [ "${TARCH}" = "x86_32" ]; then |
| cat <<EOF |
| |
| CFLAGS_${TARCH} += -march=i686 |
| EOF |
| fi |
| |
| cat <<EOF |
| |
| CPP_${TARCH}:=${GCCPREFIX}cpp |
| AS_${TARCH}:=${GCCPREFIX}as ${ASFLAGS} |
| LD_${TARCH}:=${GCCPREFIX}ld${LINKER_SUFFIX} ${LDFLAGS} |
| GCOV_${TARCH}:=${GCCPREFIX}gcov |
| EOF |
| |
| if [ "${TARCH}" = "arm64" ] && \ |
| [ -n "${LDFLAGS_ARM64_A53_ERRATUM_843419}" ]; then |
| cat <<EOF |
| |
| ifeq (\$(CONFIG_ARM64_A53_ERRATUM_843419),y) |
| LD_${TARCH}+=${LDFLAGS_ARM64_A53_ERRATUM_843419} |
| endif |
| |
| EOF |
| fi # if [ "${TARCH}" = "arm64" ]... |
| |
| cat <<EOF |
| ifeq (\$(CONFIG_COMPILER_GCC)\$(CONFIG_LP_COMPILER_GCC),y) |
| NM_${TARCH}:=${GCCPREFIX}gcc-nm |
| AR_${TARCH}:=${GCCPREFIX}gcc-ar |
| else |
| NM_${TARCH}:=${GCCPREFIX}nm |
| AR_${TARCH}:=${GCCPREFIX}ar |
| endif |
| OBJCOPY_${TARCH}:=${GCCPREFIX}objcopy |
| OBJDUMP_${TARCH}:=${GCCPREFIX}objdump |
| READELF_${TARCH}:=${GCCPREFIX}readelf |
| STRIP_${TARCH}:=${GCCPREFIX}strip |
| GNATBIND_${TARCH}:=${GCCPREFIX}gnatbind |
| CROSS_COMPILE_${TARCH}:=${GCCPREFIX} |
| |
| |
| EOF |
| #The two blank lines above are intentional separators |
| } |
| |
| # Architecture definitions |
| SUPPORTED_ARCHITECTURES="arm arm64 riscv x64 x86 ppc64" |
| |
| # TARCH: local name for the architecture |
| # (used as CC_${TARCH} in the build system) |
| # TBFDARCHS: architecture name in binutils (eg elf32-${TBFDARCH}) |
| # TCLIST: first part of the compiler triplet (eg i386 in i386-elf) |
| # TWIDTH: numerical argument for cpu mode: gcc -m${TWIDTH} |
| # TSUPP: supported subarchs (for -mcpu=...) |
| # TABI: typically elf, eabi or linux |
| |
| arch_config_arm() { |
| TARCH="arm" |
| TBFDARCHS="littlearm" |
| TCLIST="armv7-a armv7a arm" |
| TWIDTH="32" |
| TSUPP="arm armv4 armv7 armv7_m armv7_r" |
| TABI="eabi" |
| } |
| |
| arch_config_arm64() { |
| TARCH="arm64" |
| TBFDARCHS="littleaarch64" |
| TCLIST="aarch64" |
| TWIDTH="64" |
| TSUPP="arm64 armv8_64" |
| TABI="elf" |
| } |
| |
| arch_config_riscv() { |
| TARCH="riscv" |
| TBFDARCHS="littleriscv" |
| TCLIST="riscv64 riscv" |
| TWIDTH="64" |
| TABI="elf" |
| } |
| |
| arch_config_x64() { |
| TARCH="x86_64" |
| TBFDARCHS="x86-64" |
| TCLIST="x86_64" |
| TWIDTH="64" |
| TABI="elf" |
| } |
| |
| arch_config_x86() { |
| TARCH="x86_32" |
| TBFDARCHS="i386" |
| TCLIST="i386 x86_64" |
| TWIDTH="32" |
| TABI="elf" |
| CC_RT_EXTRA_GCC="--wrap __divdi3 --wrap __udivdi3 --wrap __moddi3 --wrap __umoddi3" |
| } |
| |
| arch_config_ppc64() { |
| TARCH="ppc64" |
| TBFDARCHS="powerpc" |
| TCLIST="powerpc64" |
| TWIDTH="64" |
| TSUPP="ppc64" |
| TABI="linux-gnu" # there is no generic ABI on ppc64 |
| CC_RT_EXTRA_GCC="-mcpu=power8 -mbig-endian" |
| } |
| |
| # Right now, the clang reference toolchain is not building compiler-rt builtins |
| # for any of the cross compile architectures. Hence we use libgcc for now, |
| # because that is available and lets us proceed with getting coreboot clang |
| # ready. Toggle CLANG_RUNTIME if you want to experiment with compiler-rt. |
| |
| CLANG_RUNTIME="libgcc" |
| # CLANG_RUNTIME="compiler-rt" |
| |
| test_architecture() { |
| local architecture=$1 |
| local endian gccprefix search |
| |
| GCCPREFIX="invalid" |
| unset TABI TARCH TBFDARCH TCLIST TENDIAN TSUPP TWIDTH |
| unset CC_RT_EXTRA_GCC CC_RT_EXTRA_CLANG |
| unset GCC CLANG |
| unset ARCH_SUFFIX |
| if type "arch_config_$architecture" > /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 |