#!/bin/sh
# The purpose of this dummy build test is to ensure that all the
# armeabi-v7a prebuilt binaries distributed with the NDK were
# properly built targetting VFPv3-D16, as per the ABI spec.
#
# For a related bug, see http://code.google.com/p/android/issues/detail?id=26199
#
#
# $1: ELF binary
# $2: Tag name (e.g. Tag_CPU_name)
#
extract_arch_tag ()
{
echo $($ARM_READELF -A "$1" | awk '$1 == "'$2':" { print $2; }' | sort -u | tr '\n' ' ')
}
# Returns success only if a file is a static object or library.
# We simply check the suffix, which must be either .a or .o
# $1: file name
is_static_file ()
{
case $1 in
*.o|*.a)
return 0
;;
esac
return 1
}
#
# WARNING: VERY IMPORTANT TECHNICAL NOTE:
#
# The function below works by inspecting the architecture-specific
# attributes in an ELF file. Please be aware that the behaviour of
# binutils-2.19 and binutils-2.21 is different when generating these
# tags.
#
# 1/ When compiling for ARMv7-A targets, one can use any of the following
# labels for the -mfpu=<name> option:
#
# vfp
# vfpv3
# vfpv3-d16
# neon
#
# 2/ There are two VFPv3 architectures defined by ARM:
#
# VFPv3-D16 -> Mandates only 16 double FPU registers (d0-d15)
# VFPv3-D32 -> Mandates 32 double FPU registers (d0-d31)
#
# In addition, NEON requires VFPv3-D32
#
# There is also VFPv2, which is an earlier version of VFPv3. Technically
# speaking, VFPv3 is not completely backwards compatible with VFPv2 because
# there are a few VFPv2 instructions it doesn't support.
#
# 3/ The table below indicates, for each -mfpu label, the following:
#
# - The value of the 'Tag_VFP_arch' attribute that will be placed in
# the generated object files or binaries (you can list them with
# 'readelf -A <file>')
#
# - Whether the generated code uses 16 or 32 FPU double registers
# (this is checked by looking at the disassembly of libgnustl_shared.so,
# more specifically functions like 'cosf' or 'sinf' inside it).
#
# First, for binutils-2.19:
#
# fpu value EABI tag FPU reg count
# -----------------------------------------------------
# vfp VFPv2 16
# vfpv3 VFPv3-D16 32 (*)
# vfpv3-d16 VFPv3 16 (*)
# neon VFPv3 32
#
# And now for binutils-2.21
#
# fpu value EABI tag FPU reg count
# -----------------------------------------------------
# vfp VFPv2 16
# vfpv3 VFPv3 32
# vfpv3-d16 VFPv3-D16 16
# neon VFPv3 32
#
# This shows that:
#
# - The 'VFPv3' tag seems to match VFPv3-D32 exclusively on 2.21,
# but is a mess with 2.19
#
# - Similarly, the 'vfpv3' value seems to match VFPv3-D32 as well,
# with the exception that binutils-2.19 is buggy and will put an
# invalid tag (VFPv3-D16, instead of VFPv3) in the generate ELF file.
#
# - binutils 2.19 puts the wrong tag in the executable for vfpv3 and
# vfpv3-d16, then should probably be inverted!
#
# The end result is that we can't use the EABI tag to determine the number
# of hardware FPU registers that are really used by the machine code with
# binutils 2.19 :-(
#
# BONUS:
#
# - When using 'neon', binutils-2.21 will also add a new tag named
# 'Tag_Advanced_SIMD_arch' with value 'NEONv1'. Sadly, binutils-2.19
# doesn't do any of this.
#
# Check that an ELF binary is compatible with our armeabi-v7a ABI
# (i.e. no NEON, and only 16 hardware registers being used).
#
# See technical note above to understand how this currently works.
# We're still assuming the toolchain is built with the buggy binutils-2.19.
#
# $1: path to an ARMv7-A ELF binary (static lib, shared lib or executable)
#
check_armv7_elf_binary ()
{
# We use a small awk script to parse the output of 'readelf -A'
# Which typically looks like:
#
# Attribute Section: aeabi
# File Attributes
# Tag_CPU_name: "7-A"
# Tag_CPU_arch: v7
# Tag_CPU_arch_profile: Application
# Tag_ARM_ISA_use: Yes
# Tag_THUMB_ISA_use: Thumb-2
# Tag_VFP_arch: VFPv3-D16
# Tag_ABI_PCS_wchar_t: 4
# Tag_ABI_FP_denormal: Needed
# Tag_ABI_FP_exceptions: Needed
# Tag_ABI_FP_number_model: IEEE 754
# Tag_ABI_align8_needed: Yes
# Tag_ABI_align8_preserved: Yes, except leaf SP
# Tag_ABI_enum_size: int
# Tag_ABI_HardFP_use: SP and DP
# Tag_ABI_optimization_goals: Aggressive Speed
# Tag_unknown_44: 1 (0x1)
#
# Note that for static libraries, these sections will appear multiple
# time in the output of 'readelf -A'.
echo "Checking: $(basename $1)"
if [ ! -f "$1" ]; then
1>&2 echo "PANIC: Missing binary: $1"
exit 1
fi
# We want to check the values of Tag_CPU_name
CPU_NAMES=$(extract_arch_tag "$1" Tag_CPU_name)
VFP_ARCHS=$(extract_arch_tag "$1" Tag_VFP_arch)
NEON_ARCHS=$(extract_arch_tag "$1" Tag_Advanced_SIMD_arch)
# IMPORTANT NOTE: Even when using -march=armv7-a, the compiler may not
# necessarily use ARMv7-A specific instruction and will tag an object file
# with the following attributes:
#
# Attribute Section: aeabi
# File Attributes
# Tag_CPU_name: "5TE"
# Tag_CPU_arch: v5TE
# Tag_ARM_ISA_use: Yes
# Tag_THUMB_ISA_use: Thumb-1
# Tag_ABI_PCS_wchar_t: 4
# Tag_ABI_FP_denormal: Needed
# Tag_ABI_FP_exceptions: Needed
# Tag_ABI_FP_number_model: IEEE 754
# Tag_ABI_align8_needed: Yes
# Tag_ABI_align8_preserved: Yes, except leaf SP
# Tag_ABI_enum_size: int
# Tag_ABI_optimization_goals: Aggressive Speed
# Tag_unknown_44: 1 (0x1)
#
# This means that in static libraries, you can have both
# '5TE' and '7-A' CPU name tags at the same time, or only
# '5TE' or only '7-A', deal with all these cases properly.
echo " found tags: CPU names:'$CPU_NAMES' VFP:'$VFP_ARCHS' NEON:'$NEON_ARCHS'"
# Clearly, any trace of NEON is a deal-breaker!
if [ "$NEON_ARCHS" ]; then
1>&2 echo "PANIC: Binary file should not contain NEON instructions: $1"
exit 1
fi
if is_static_file "$1"; then
# For static libraries / object files, it's ok to contain ARMv5TE binaries
if [ "$CPU_NAMES" == "\"5TE\"" -a "$CPU_NAMES" != "\"7-A\"" -a "$CPU_NAMES" != "\"5TE\" \"7-A\"" ]; then
# Neither ARMv7-A or ARMv5TE+ARMv7-A, something's fishy
1>&2 echo "PANIC: File is neither ARMv5TE or ARMv7-A binary: $1"
exit 1
fi
# exit here because some static libraries can have a mix of several
# VFP tags that make them difficult to check (e.g. libgnustl_static.a
# can have 'VFPv1 VFPv2 VFPv3' at the same time :-(
return
fi
# If we reach this point, we only contain ARMv7-A machine code, so look
# at the VFP arch tag(s)
# Sometimes no VFP_arch tag is placed in the final binary, this happens
# with libgabi++_shared.so for example, because the code doesn't have
# any floating point instructions.
#
# XXX: FOR NOW, ASSUME BROKEN binutils-2.19, AND THUS THAT 'VFPv3' IS VALID
if [ "$VFP_ARCHS" != "VFPv3" -a "$VFP_ARCHS" != "VFPv3-D16" -a "$VFP_ARCHS" != "" ]; then
1>&2 echo "PANIC: File is not a VFPv3-D16 binary: $1"
exit 1
fi
}
export ANDROID_NDK_ROOT=$NDK
NDK_BUILDTOOLS_PATH=$NDK/build/tools
. $NDK/build/tools/prebuilt-common.sh
if [ -n "$APP_ABI" ]; then
if [ "$(convert_abi_to_arch $APP_ABI)" != "arm" ]; then
echo "Skipping ARM only test"
exit 0
fi
fi
ARM_TOOLCHAIN_NAME=$(get_default_toolchain_name_for_arch arm)
ARM_TOOLCHAIN_PREFIX=$(get_default_toolchain_prefix_for_arch arm)
case $(uname -s) in
Darwin)
HOST_ARCH=`uname -m`
case "$HOST_ARCH" in
i?86) HOST_ARCH=x86
if ! echo __LP64__ | (CCOPTS= gcc -E - 2>/dev/null) | grep -q __LP64__ ; then
HOST_ARCH=x86_64
fi
;;
esac
HOST_TAG=darwin-$HOST_ARCH
;;
Linux)
HOST_TAG=linux-$(uname -p)
;;
*)
echo "WARNING: This test cannot run on this machine!" >&2
exit 0
;;
esac
ARM_READELF=$NDK/toolchains/$ARM_TOOLCHAIN_NAME/prebuilt/$HOST_TAG/bin/${ARM_TOOLCHAIN_PREFIX}-readelf
if [ ! -f "$ARM_READELF" ]; then
echo "ERROR: Missing binary: $ARM_READELF" >&2
exit 1
fi
ARMv7_ABIS="armeabi-v7a armeabi-v7a-hard"
for ABI in $ARMv7_ABIS; do
STLPORT_LIBS=$NDK/sources/cxx-stl/stlport/libs/$ABI
check_armv7_elf_binary $STLPORT_LIBS/libstlport_shared.so
check_armv7_elf_binary $STLPORT_LIBS/libstlport_static.a
for VERSION in $DEFAULT_GCC_VERSION_LIST; do
GNUSTL_LIBS=$NDK/sources/cxx-stl/gnu-libstdc++/4.9/libs/$ABI
check_armv7_elf_binary $GNUSTL_LIBS/libsupc++.a
check_armv7_elf_binary $GNUSTL_LIBS/libgnustl_shared.so
check_armv7_elf_binary $GNUSTL_LIBS/libgnustl_static.a
done
done
echo "Done!"