| #!/usr/bin/env bash |
| # |
| # This file is part of the coreboot project. |
| # |
| # Copyright (C) 2007-2010 coresystems GmbH |
| # Copyright (C) 2012 Google Inc |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; version 2 of the License. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software |
| # Foundation, Inc. |
| # |
| |
| TMPFILE="" |
| XGCCPATH=${1:-"`pwd`/util/crossgcc/xgcc/bin/"} |
| |
| die() { |
| echo "ERROR: $*" >&2 |
| exit 1 |
| } |
| |
| clean_up() { |
| if [ -n "$TMPFILE" ]; then |
| rm -f "$TMPFILE" "$TMPFILE.c" "$TMPFILE.o" |
| fi |
| } |
| |
| # Create temporary file(s). |
| TMPFILE="$(mktemp /tmp/temp.XXXX 2>/dev/null || echo /tmp/temp.78gOIUGz)" |
| touch "$TMPFILE" |
| 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 |
| else |
| die "no iasl found" |
| fi |
| |
| if program_exists gcc; then |
| HOSTCC=gcc |
| elif program_exists cc; then |
| HOSTCC=cc |
| else |
| die "no host compiler found" |
| fi |
| |
| cat <<EOF |
| # platform agnostic and host tools |
| IASL:=${IASL} |
| HOSTCC?=${HOSTCC} |
| |
| EOF |
| |
| testcc() { |
| local tmp_c="$TMPFILE.c" |
| local tmp_o="$TMPFILE.o" |
| rm -f "$tmp_c" "$tmp_o" |
| echo "void _start(void) {}" >"$tmp_c" |
| $1 -nostdlib -Werror $2 -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 $1 $2 && |
| $3 -nostdlib -static $4 -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="$(${gccprefix}objdump -p $obj_file)" |
| local obj_arch="$(expr "$obj_type" : '.*format \(.[a-z0-9-]*\)')" |
| [ "$obj_arch" = "$full_arch" ] || return 1 |
| |
| # Architecture matched. |
| GCCPREFIX="$gccprefix" |
| |
| unset ASFLAGS LDFLAGS |
| unset CFLAGS_GCC CFLAGS_CLANG |
| |
| if [ -n "$use_dash_twidth" ]; then |
| ASFLAGS="--$twidth" |
| CFLAGS_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" |
| CFLAGS_GCC="$CFLAGS_GCC -Wl,-b,elf32-i386 -Wl,-melf_i386" |
| CFLAGS_CLANG="$CFLAGS_GCC -Wl,-b,elf32-i386 -Wl,-melf_i386" |
| ;; |
| esac |
| |
| return 0 |
| } |
| |
| detect_special_flags() { |
| local architecture="$1" |
| # GCC 4.6 is much more picky about unused variables. |
| # Turn off it's warnings for now: |
| testcc "$GCC" "$CFLAGS_GCC -Wno-unused-but-set-variable " && |
| CFLAGS_GCC="$CFLAGS_GCC -Wno-unused-but-set-variable " |
| |
| # Check for an operational -m32/-m64 |
| testcc "$GCC" "$CFLAGS_GCC -m$TWIDTH " && |
| CFLAGS_GCC="$CFLAGS_GCC -m$TWIDTH " |
| |
| # Use bfd linker instead of gold if available: |
| testcc "$GCC" "$CFLAGS_GCC -fuse-ld=bfd" && |
| CFLAGS_GCC="$CFLAGS_GCC -fuse-ld=bfd" && LINKER_SUFFIX='.bfd' |
| |
| testcc "$GCC" "$CFLAGS_GCC -fno-stack-protector"&& |
| CFLAGS_GCC="$CFLAGS_GCC -fno-stack-protector" |
| testcc "$GCC" "$CFLAGS_GCC -Wl,--build-id=none" && |
| CFLAGS_GCC="$CFLAGS_GCC -Wl,--build-id=none" |
| |
| case "$architecture" in |
| x86) |
| # Always 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. |
| CFLAGS_GCC="$CFLAGS_GCC -march=i686" |
| ;; |
| x64) |
| ;; |
| arm64) |
| ;; |
| mipsel) |
| testcc "$GCC" "$CFLAGS_GCC -mno-abicalls -fno-pic" && \ |
| CFLAGS_GCC+=" -mno-abicalls -fno-pic" |
| |
| # Enforce little endian mode. |
| testcc "$GCC" "$CFLAGS_GCC -EL" && \ |
| CFLAGS_GCC+=" -EL" |
| ;; |
| esac |
| } |
| |
| detect_compiler_runtime() { |
| test -z "$CLANG" || \ |
| CC_RT_CLANG="`${CLANG} ${CFLAGS_CLANG} -print-librt-file-name 2>/dev/null`" |
| test -z "$GCC" || \ |
| CC_RT_GCC="`${GCC} ${CFLAGS_GCC} -print-libgcc-file-name`" |
| } |
| |
| report_arch_toolchain() { |
| cat <<EOF |
| # elf${TWIDTH}-${TBFDARCH} toolchain (${GCC}) |
| ARCH_SUPPORTED+=${TARCH} |
| SUBARCH_SUPPORTED+=${TSUPP-${TARCH}} |
| CC_${TARCH}:=${GCC} |
| CFLAGS_${TARCH}:=${CFLAGS_GCC} |
| COMPILER_RT_${TARCH}:=${CC_RT_GCC} |
| COMPILER_RT_FLAGS_${TARCH}:=${CC_RT_EXTRA_GCC} |
| CPP_${TARCH}:=${GCCPREFIX}cpp |
| AS_${TARCH}:=${GCCPREFIX}as ${ASFLAGS} |
| LD_${TARCH}:=${GCCPREFIX}ld${LINKER_SUFFIX} ${LDFLAGS} |
| NM_${TARCH}:=${GCCPREFIX}nm |
| OBJCOPY_${TARCH}:=${GCCPREFIX}objcopy |
| OBJDUMP_${TARCH}:=${GCCPREFIX}objdump |
| READELF_${TARCH}:=${GCCPREFIX}readelf |
| STRIP_${TARCH}:=${GCCPREFIX}strip |
| AR_${TARCH}:=${GCCPREFIX}ar |
| CROSS_COMPILE_${TARCH}:=${GCCPREFIX} |
| |
| EOF |
| } |
| |
| # Architecture definitions |
| SUPPORTED_ARCHITECTURES="arm arm64 mipsel riscv x64 x86" |
| |
| arch_config_arm() { |
| TARCH="arm" |
| TBFDARCHS="littlearm" |
| TCLIST="armv7-a armv7a" |
| TWIDTH="32" |
| TSUPP="arm armv4 armv7 armv7_m" |
| 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="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_mipsel() { |
| TARCH="mips" |
| TBFDARCHS="tradlittlemips littlemips" |
| TCLIST="mipsel" |
| TWIDTH="32" |
| TSUPP="mips mipsel" |
| TABI="elf" |
| TENDIAN="EL" |
| } |
| |
| 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-" |
| done |
| echo "# $architecture TARCH_SEARCH=$search" |
| |
| # Search toolchain by checking assembler capability. |
| for TBFDARCH in $TBFDARCHS; do |
| for gccprefix in $search ""; do |
| program_exists "${gccprefix}as" || continue |
| for endian in $TENDIAN ""; do |
| testas "$gccprefix" "$TWIDTH" "$TBFDARCH" \ |
| "" "$endian" && break 3 |
| testas "$gccprefix" "$TWIDTH" "$TBFDARCH" \ |
| "TRUE" "$endian" && break 3 |
| done |
| done |
| done |
| if [ "invalid" != "$GCCPREFIX" ]; then |
| GCC="${GCCPREFIX}gcc" |
| fi |
| |
| for clang_arch in $TCLIST invalid; do |
| testcc "clang" "-target ${clang_arch}-$TABI -c" && break |
| 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 -target ${clang_arch}-${TABI} -ccc-gcc-name ${GCC}" |
| fi |
| |
| if [ -z "$GCC" -a -z "$CLANG" ]; then |
| echo "Warning: no suitable compiler for $architecture." >&2 |
| return 1 |
| fi |
| } |
| |
| # This loops over all supported architectures. |
| for architecture in $SUPPORTED_ARCHITECTURES; do |
| if test_architecture $architecture; then |
| detect_special_flags "$architecture" |
| detect_compiler_runtime "$architecture" |
| report_arch_toolchain |
| fi |
| done |