Skip to content

Commit

Permalink
multilib: Determine which options may pass through.
Browse files Browse the repository at this point in the history
On some arches (e.g. MIPS) the options like -mabi do not work if
specified more than once (see the comment in 100-gcc.sh). Therefore,
we need to determine which of the options produced by <arch>.sh can
be passed to multilib builds and which must be removed (i.e., which
options vary among the multilibs).

This presents a chicken-and-egg problem. GCC developers, in their
infinite wisdom, do not allow arbitrary multilib specification to be
supplied to GCC's configure. Instead, the target (and sometimes some
extra options) determine the set of multilibs - which may include
different CPUs, different ABIs, different endianness, different FPUs,
different floating-point ABIs, ... That is, we don't know which parts
vary until we build GCC and ask it.

So, the solution implemented here is:
- For multilib builds, start with empty CT_ARCH_TARGET_CFLAGS/LDFLAGS.
- For multilib builds, require core pass 1. Pass 1 does not build any
  target binaries, so at that point, our target options have not been
  used yet.
- Provide an API to modify the environment variables for the steps that
  follow the current one.
- As a part of multilib-related housekeeping, determine the variable
  part of multilibs and filter out these options; pass the rest into
  CT_TARGET_CFLAGS/LDFLAGS.

This still does not handle extra dependencies between GCC options (like
-ma implying -mcpu=X -mtune=Y, etc.) but I feel that would complicate
matters too much. Let's leave this until there's a compelling case for
it.

Also, query GCC's sysroot suffix for targets that use it (SuperH,
for example) - the default multilib may not work if the command line
specifies the default option explicitly (%sysroot_suffix_spec is not
aware of multilib defaults).

Signed-off-by: Alexey Neyman <stilor@att.net>
  • Loading branch information
stilor committed Aug 23, 2016
1 parent cc86d80 commit 82072d0
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 55 deletions.
4 changes: 4 additions & 0 deletions config/target.in
Expand Up @@ -65,9 +65,13 @@ config ARCH_REQUIRES_MULTILIB
bool
select MULTILIB

# Multilib requires 1st core pass (i.e., pass without building libgcc)
# to determine which target cflags vary with multilib and which must be
# passed from the arch configuration.
config MULTILIB
bool
prompt "Build a multilib toolchain (READ HELP!!!)"
select CC_CORE_PASS_1_NEEDED
help
If you say 'y' here, then the toolchain will also contain the C library
optimised for some variants of the selected architecture, besides the
Expand Down
218 changes: 167 additions & 51 deletions scripts/build/cc/100-gcc.sh
Expand Up @@ -84,6 +84,167 @@ cc_gcc_lang_list() {
printf "%s" "${lang_list}"
}

#------------------------------------------------------------------------------
# Return a value of a requested GCC spec
cc_gcc_get_spec() {
local spec=$1
local cc_and_cflags=$2

# GCC does not provide a facility to request a value of a spec string.
# The only way to do that I found was to augment the spec file with
# some dummy suffix handler that does nothing except printing it.
touch temp-input.spec_eval
{
echo ".spec_eval:"
echo "echo %(${spec})"
} > "tmp-specs-${spec}"
${cc_and_cflags} -specs="tmp-specs-${spec}" -E temp-input.spec_eval
}

#------------------------------------------------------------------------------
# Report the type of a GCC option
cc_gcc_classify_opt() {
# Options present in multiple architectures
case "${1}" in
-march=*) echo "arch"; return;;
-mabi=*) echo "abi"; return;;
-mcpu=*|-mmcu=*) echo "cpu"; return;;
-mtune=*) echo "tune"; return;;
-mfpu=*) echo "fpu"; return;;
-mhard-float|-msoft-float|-mno-soft-float|-mno-float|-mfloat-abi=*|\
-mfpu|-mno-fpu) echo "float"; return;;
-EB|-EL|-mbig-endian|-mlittle-endian|-mbig|-mlittle|-meb|-mel|-mb|-ml) echo "endian"; return;;
-mthumb|-marm) echo "mode"; return;;
esac

# Arch-specific options and aliases
case "${CT_ARCH}" in
m68k)
case "${1}" in
-m68881) echo "float"; return;;
-m5[234]*|-mcfv4e) echo "cpu"; return;;
-m68*|-mc68*) echo "arch"; return;;
esac
;;
mips)
case "${1}" in
-mips[1234]|-mips32|-mips32r*|-mips64|-mips64r*) echo "cpu"; return;;
esac
;;
sh)
case "${1}" in
-m[12345]*) echo "cpu"; return;;
esac
esac

# All tried and failed
echo "unknown"
}

#------------------------------------------------------------------------------
# This function lists the multilibs configured in the compiler (even if multilib
# is disabled - so that it lists the default GCC/OS directory, which may differ
# from the default 'lib'). It then performs a few multilib checks/quirks:
#
# 1. On SuperH target, configuring with default CPU (e.g. by supplying the target
# name as 'sh4', which is what CT-NG does) results in the compiler being unable to
# run if that same switch is passed to the resulting gcc (e.g. 'gcc -m4'). The reason
# for this behavior is that the script that determines the sysroot suffix is not
# aware of the default multilib selection, so it generates <sysroot>/m4 as the
# suffixed sysroot. But the main driver, knowing that -m4 is the default, does not
# even attempt to fall back to the non-suffixed sysroot (as it does with non-default
# multilibs) - as a result, gcc fails to find any library if invoked with -m4.
# The right solution would be to drop the default CPU from the multilib list
# completely, or make the print-sysroot-suffix.sh script aware of the defaults
# (which is not easy, as the defaults are not in tmake_file, but rather in tm_file...)
#
# 2. On MIPS target, gcc (or rather, ld, which it invokes under the hood) chokes
# if supplied with two -mabi=* options. I.e., 'gcc -mabi=n32' and 'gcc -mabi=32' both
# work, but 'gcc -mabi=32 -mabi=n32' produces an internal error in ld. Thus we do
# not supply target's CFLAGS in multilib builds - and after compiling pass-1 gcc,
# attempt to determine which CFLAGS need to be filtered out.
cc_gcc_multilib_housekeeping() {
local cc host
local flags osdir dir multilibs i f
local multilib_defaults
local suffix sysroot base lnk
local ml_arch ml_abi ml_cpu ml_tune ml_fpu ml_float ml_endian ml_mode ml_unknown ml
local new_cflags

for arg in "$@"; do
eval "${arg// /\\ }"
done

if [ \( "${CT_CANADIAN}" = "y" -o "${CT_CROSS_NATIVE}" = "y" \) -a "${host}" = "${CT_HOST}" ]; then
CT_DoLog EXTRA "Canadian Cross/Cross-native unable to confirm multilibs configuration "\
"directly; will use build-compiler for housekeeping."
# Since we cannot run the desired compiler, substitute build-CC with the assumption
# that the host-CC is configured in the same way.
cc="${CT_BUILDTOOLS_PREFIX_DIR}/bin/${CT_TARGET}-gcc"
fi

# sed: prepend dashes or do nothing if default is empty string
multilib_defaults=( $( cc_gcc_get_spec multilib_defaults "${cc}" | \
${sed} 's/\(^\|[[:space:]]\+\)\([^[:space:]]\)/ -\2/g' ) )
CT_DoLog EXTRA "gcc default flags: '${multilib_defaults}'"

multilibs=( $( "${cc}" -print-multi-lib ) )
if [ ${#multilibs[@]} -ne 0 ]; then
CT_DoLog EXTRA "gcc configured with these multilibs (including the default):"
for i in "${multilibs[@]}"; do
dir="${i%%;*}"
flags="${i#*;}"
flags=${flags//@/ -}
osdir=$( "${cc}" -print-multi-os-directory ${flags} )
CT_DoLog EXTRA " '${flags}' --> lib/${dir}/ (gcc) lib/${osdir} (os)"
for f in ${flags}; do
eval ml_`cc_gcc_classify_opt ${f}`=seen
done
done
else
CT_DoLog WARN "no multilib configuration: GCC unusable?"
fi

# Filtering out some of the options provided in CT-NG config. Then *prepend*
# them to CT_TARGET_CFLAGS, like scripts/crosstool-NG.sh does. Zero out
# the stashed MULTILIB flags so that we don't process them again in the passes
# that follow.
CT_DoLog DEBUG "Configured target CFLAGS: '${CT_ARCH_TARGET_CFLAGS_MULTILIB}'"
ml_unknown= # Pass through anything we don't know about
for f in ${CT_ARCH_TARGET_CFLAGS_MULTILIB}; do
eval ml=\$ml_`cc_gcc_classify_opt ${f}`
if [ "${ml}" != "seen" ]; then
new_cflags="${new_cflags} ${f}"
fi
done
CT_DoLog DEBUG "Filtered target CFLAGS: '${new_cflags}'"
CT_EnvModify CT_TARGET_CFLAGS "${new_cflags} ${CT_TARGET_CFLAGS}"
CT_EnvModify CT_ARCH_TARGET_CFLAGS_MULTILIB ""

# Currently, the only LDFLAGS are endianness-related
CT_DoLog DEBUG "Configured target LDFLAGS: '${CT_ARCH_TARGET_LDFLAGS_MULTILIB}'"
if [ "${ml_endian}" != "seen" ]; then
CT_EnvModify CT_TARGET_LDFLAGS "${CT_ARCH_TARGET_LDFLAGS_MULTILIB} ${CT_TARGET_LDFLAGS}"
CT_EnvModify CT_ARCH_TARGET_LDFLAGS_MULTILIB ""
fi
CT_DoLog DEBUG "Filtered target LDFLAGS: '${CT_ARCH_TARGET_LDFLAGS_MULTILIB}'"

# Sysroot suffix fixup for the multilib default.
suffix=$( cc_gcc_get_spec sysroot_suffix_spec "${cc} ${multilib_defaults}" )
if [ -n "${suffix}" ]; then
base=${suffix%/*}
sysroot=$( "${cc}" -print-sysroot )
if [ -n "${base}" ]; then
CT_DoExecLog ALL mkdir -p "${sysroot}${base}"
lnk=$( echo "${base#/}" | ${sed} -e 's,[^/]*,..,g' )
else
lnk=.
fi
CT_DoExecLog ALL rm -f "${sysroot}${suffix}"
CT_DoExecLog ALL ln -sfv "${lnk}" "${sysroot}${suffix}"
fi
}

#------------------------------------------------------------------------------
# Core gcc pass 1
do_gcc_core_pass_1() {
Expand Down Expand Up @@ -201,9 +362,6 @@ do_gcc_core_backend() {
local -a core_targets_install
local -a extra_user_config
local arg
local dir
local flags
local osdir

for arg in "$@"; do
eval "${arg// /\\ }"
Expand Down Expand Up @@ -542,7 +700,7 @@ do_gcc_core_backend() {
# tree makes the libtoolized utilities that are built next assume
# that, for example, libsupc++ is an "accessory library", and not include
# -lsupc++ to the link flags. That breaks ltrace, for example.
CT_DoLog EXTRA "Housekeeping for final gcc compiler"
CT_DoLog EXTRA "Housekeeping for core gcc compiler"
CT_Pushd "${prefix}"
find . -type f -name "*.la" -exec rm {} \; |CT_DoLog ALL
CT_Popd
Expand All @@ -563,32 +721,8 @@ do_gcc_core_backend() {
CT_DoExecLog ALL ln -sfv "${CT_TARGET}-gcc${ext}" "${prefix}/bin/${CT_TARGET}-cc${ext}"
fi

if [ "${CT_CANADIAN}" = "y" -a "${mode}" = "baremetal" \
-a "${host}" = "${CT_HOST}" ]; then
CT_DoLog EXTRA "Canadian Cross unable to confirm multilibs configured correctly"
else
multilibs=( $( "${prefix}/bin/${CT_TARGET}-gcc" -print-multi-lib ) )
if [ ${#multilibs[@]} -ne 0 ]; then
CT_DoLog EXTRA "gcc configured with these multilibs (including the default):"
for i in "${multilibs[@]}"; do
dir="${i%%;*}"
flags="${i#*;}"
flags=${flags//@/ -}
osdir=$( "${prefix}/bin/${CT_TARGET}-gcc" -print-multi-os-directory ${flags} )
CT_DoLog EXTRA " '${flags}' --> lib/${dir}/ (gcc) lib/${osdir} (os)"
# When building core GCC, create the necessary directories for libc & friends.
case "${build_step}" in
core1|core2)
CT_DoExecLog ALL mkdir -p "${CT_PREFIX_DIR}/lib/${osdir}"
CT_DoExecLog ALL mkdir -p "${CT_SYSROOT_DIR}/lib/${osdir}"
CT_DoExecLog ALL mkdir -p "${CT_SYSROOT_DIR}/usr/lib/${osdir}"
;;
esac
done
else
CT_DoLog WARN "no multilib configuration: GCC unusable?"
fi
fi
cc_gcc_multilib_housekeeping cc="${prefix}/bin/${CT_TARGET}-gcc" \
host="${host}"
}

#------------------------------------------------------------------------------
Expand Down Expand Up @@ -695,9 +829,6 @@ do_gcc_backend() {
local -a final_LDFLAGS
local tmp
local arg
local dir
local flags
local osdir

for arg in "$@"; do
eval "${arg// /\\ }"
Expand Down Expand Up @@ -978,24 +1109,9 @@ do_gcc_backend() {
file="$( ls -1 "${CT_PREFIX_DIR}/bin/${CT_TARGET}-gcc."* 2>/dev/null || true )"
[ -z "${file}" ] || ext=".${file##*.}"
if [ -f "${CT_PREFIX_DIR}/bin/${CT_TARGET}-gcc${ext}" ]; then
CT_DoExecLog ALL ln -sfv "${CT_TARGET}-gcc${ext}" "${CT_PREFIX_DIR}/bin/${CT_TARGET}-cc${ext}"
CT_DoExecLog ALL ln -sfv "${CT_TARGET}-gcc${ext}" "${prefix}/bin/${CT_TARGET}-cc${ext}"
fi

if [ "${CT_CANADIAN}" = "y" ]; then
CT_DoLog EXTRA "Canadian Cross unable to confirm multilibs configured correctly"
else
multilibs=( $( "${prefix}/bin/${CT_TARGET}-gcc" -print-multi-lib ) )
if [ ${#multilibs[@]} -ne 0 ]; then
CT_DoLog EXTRA "gcc configured with these multilibs (including the default):"
for i in "${multilibs[@]}"; do
dir="${i%%;*}"
flags="${i#*;}"
flags=${flags//@/ -}
osdir=$( "${prefix}/bin/${CT_TARGET}-gcc" -print-multi-os-directory $flags )
CT_DoLog EXTRA " '${flags}' --> lib/${dir}/ (gcc) lib/${osdir} (os)"
done
else
CT_DoLog WARN "no multilib configuration: GCC unusable?"
fi
fi
cc_gcc_multilib_housekeeping cc="${prefix}/bin/${CT_TARGET}-gcc" \
host="${host}"
}
22 changes: 19 additions & 3 deletions scripts/build/libc/glibc.sh
Expand Up @@ -111,13 +111,29 @@ do_libc_backend() {
# (default target, not multilib)
multi_last=y
fi

# GCC makes the distinction between:
# multilib (-print-multi-lib or -print-multi-directory) and
# multilib-os (--print-multi-os-directory)
# as the gcc library and gcc sysroot library paths, respectively.
# For example, on x86_64:
# multilib: -m32=32 -m64=.
# multilib-os: -m32=../lib -m64=../lib64
# Moreover, while some multilibs can coexist in the same sysroot (e.g.
# on x86), some have a "sysroot suffix" to separate incompatible variants.
# Such sysroot suffixes combine with multilib-os directories, e.g.
# on sh4 with -m4a multilib, the search order in sysroot is (dropping some
# directories for brevity:
# <sysroot>/m4a/lib/m4a/
# <sysroot>/m4a/usr/lib/m4a/
# <sysroot>/m4a/lib/
# <sysroot>/m4a/usr/lib/
multi_flags=$( echo "${multilib#*;}" | ${sed} -r -e 's/@/ -/g;' )
multi_dir="${multilib%%;*}"
if [ "${multi_dir}" != "." ]; then
CT_DoStep INFO "Building for multilib subdir='${multi_dir}'"

extra_flags="$( echo "${multilib#*;}" \
|${sed} -r -e 's/@/ -/g;' \
)"
extra_flags="${multi_flags}"
extra_dir="/${multi_dir}"

# glibc install its files in ${extra_dir}/{usr/,}lib
Expand Down
10 changes: 9 additions & 1 deletion scripts/crosstool-NG.sh.in
Expand Up @@ -292,7 +292,7 @@ if [ -z "${CT_RESTART}" ]; then
*/*) CT_Abort "Sysroot name contains forbidden slash(es): '${CT_SYSROOT_NAME}'";;
esac

# Arrange paths depending on wether we use sysroot or not.
# Arrange paths depending on whether we use sysroot or not.
if [ "${CT_USE_SYSROOT}" = "y" ]; then
CT_SYSROOT_REL_DIR="${CT_SYSROOT_DIR_PREFIX:+${CT_SYSROOT_DIR_PREFIX}/}${CT_SYSROOT_NAME}"
CT_SYSROOT_DIR="${CT_PREFIX_DIR}/${CT_TARGET}/${CT_SYSROOT_REL_DIR}"
Expand Down Expand Up @@ -628,6 +628,14 @@ if [ "${CT_ONLY_DOWNLOAD}" != "y" -a "${CT_ONLY_EXTRACT}" != "y" ]; then
# sub-shell ending with !0. bash-3 does not, while bash-4 does,
# so the following line is for bash-3; bash-4 would choke above.
[ $? -eq 0 ]
# Pick up environment changes.
if [ -r "${CT_BUILD_DIR}/env.modify.sh" ]; then
CT_DoLog DEBUG "Step '${step}' modified the environment:"
CT_DoExecLog DEBUG cat "${CT_BUILD_DIR}/env.modify.sh"
. "${CT_BUILD_DIR}/env.modify.sh"
CT_DoExecLog DEBUG rm -f "${CT_BUILD_DIR}/env.modify.sh"

fi
if [ "${CT_STOP}" = "${step}" ]; then
do_stop=1
fi
Expand Down
25 changes: 25 additions & 0 deletions scripts/functions
Expand Up @@ -1183,6 +1183,14 @@ CT_DoConfigSub() {
fi
}

# Normally, each step is executed in a sub-shell and thus cannot modify the
# environment for the next step(s). When this is needed, it can do so by
# invoking this function.
# Usage: CT_EnvModify VAR VALUE
CT_EnvModify() {
echo "${1}=\"${2}\"" >> "${CT_BUILD_DIR}/env.modify.sh"
}

# Compute the target tuple from what is provided by the user
# Usage: CT_DoBuildTargetTuple
# In fact this function takes the environment variables to build the target
Expand Down Expand Up @@ -1303,6 +1311,23 @@ CT_DoBuildTargetTuple() {

# Now on for the target LDFLAGS
CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"

# Now, a multilib quirk. We may not be able to pass CT_ARCH_TARGET_CFLAGS
# and CT_ARCH_TARGET_LDFLAGS to gcc: even though GCC build appends the multilib
# flags afterwards, on some architectures the build breaks because some
# flags do not completely override each other. For example, on mips target,
# 'gcc -mabi=32' and 'gcc -mabi=n32' both work, but 'gcc -mabi=32 -mabi=n32'
# triggers an internal linker error. Likely a bug in GNU binutils, but we
# have to work it around for now: *do not pass the CT_ARCH_TARGET_ flags*.
# Instead, save them into a different variable here. Then, after the first
# core pass, we'll know which of them vary with multilibs (i.e. must be
# filtered out).
if [ "${CT_MULTILIB}" = "y" ]; then
CT_ARCH_TARGET_CFLAGS_MULTILIB="${CT_ARCH_TARGET_CFLAGS}"
CT_ARCH_TARGET_CFLAGS=
CT_ARCH_TARGET_LDFLAGS_MULTILIB="${CT_ARCH_TARGET_LDFLAGS}"
CT_ARCH_TARGET_LDFLAGS=
fi
}

# This function determines the target tuple for a given set of compiler
Expand Down

0 comments on commit 82072d0

Please sign in to comment.