479 lines
12 KiB
Bash
Executable File
479 lines
12 KiB
Bash
Executable File
#!/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}/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}
|
|
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"
|
|
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"
|
|
;;
|
|
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}}
|
|
|
|
# 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}
|
|
|
|
# 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_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 -malign-data=abi
|
|
EOF
|
|
fi
|
|
|
|
if [ "${TARCH}" = "x86_32" ]; then
|
|
cat <<EOF
|
|
|
|
ifneq (\$(CONFIG_USE_MARCH_586)\$(CONFIG_LP_USE_MARCH_586),)
|
|
CFLAGS_${TARCH} += -march=i586
|
|
else
|
|
CFLAGS_${TARCH} += -march=i686
|
|
endif
|
|
EOF
|
|
fi
|
|
|
|
cat <<EOF
|
|
|
|
CPP_${TARCH}:=${GCCPREFIX}cpp
|
|
AS_${TARCH}:=${GCCPREFIX}as ${ASFLAGS}
|
|
LD_${TARCH}:=${GCCPREFIX}ld${LINKER_SUFFIX} ${LDFLAGS}
|
|
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
|
|
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 -ccc-gcc-name ${GCC}"
|
|
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
|