#!/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= 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 ') # # - 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 } . $NDK/build/tools/dev-defaults.sh 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_TAG=darwin-$(uname -m) ;; 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 GABIXX_LIBS=$NDK/sources/cxx-stl/gabi++/libs/$ABI check_armv7_elf_binary $GABIXX_LIBS/libgabi++_shared.so check_armv7_elf_binary $GABIXX_LIBS/libgabi++_static.a 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++/$VERSION/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!"