1#!/bin/sh
2
3# The purpose of this dummy build test is to ensure that all the
4# armeabi-v7a prebuilt binaries distributed with the NDK were
5# properly built targetting VFPv3-D16, as per the ABI spec.
6#
7# For a related bug, see http://code.google.com/p/android/issues/detail?id=26199
8#
9
10#
11# $1: ELF binary
12# $2: Tag name (e.g. Tag_CPU_name)
13#
14extract_arch_tag ()
15{
16    echo $($ARM_READELF -A "$1" | awk '$1 == "'$2':" { print $2; }' | sort -u | tr '\n' ' ')
17}
18
19# Returns success only if a file is a static object or library.
20# We simply check the suffix, which must be either .a or .o
21# $1: file name
22is_static_file ()
23{
24    case $1 in
25        *.o|*.a)
26            return 0
27            ;;
28    esac
29    return 1
30}
31
32
33#
34# WARNING: VERY IMPORTANT TECHNICAL NOTE:
35#
36# The function below works by inspecting the architecture-specific
37# attributes in an ELF file. Please be aware that the behaviour of
38# binutils-2.19 and binutils-2.21 is different when generating these
39# tags.
40#
41# 1/ When compiling for ARMv7-A targets, one can use any of the following
42#    labels for the -mfpu=<name> option:
43#
44#        vfp
45#        vfpv3
46#        vfpv3-d16
47#        neon
48#
49# 2/ There are two VFPv3 architectures defined by ARM:
50#
51#        VFPv3-D16  -> Mandates only 16 double FPU registers (d0-d15)
52#        VFPv3-D32  -> Mandates 32 double FPU registers (d0-d31)
53#
54#    In addition, NEON requires VFPv3-D32
55#
56#    There is also VFPv2, which is an earlier version of VFPv3. Technically
57#    speaking, VFPv3 is not completely backwards compatible with VFPv2 because
58#    there are a few VFPv2 instructions it doesn't support.
59#
60# 3/ The table below indicates, for each -mfpu label, the following:
61#
62#     - The value of the 'Tag_VFP_arch' attribute that will be placed in
63#       the generated object files or binaries (you can list them with
64#       'readelf -A <file>')
65#
66#     - Whether the generated code uses 16 or 32 FPU double registers
67#       (this is checked by looking at the disassembly of libgnustl_shared.so,
68#       more specifically functions like 'cosf' or 'sinf' inside it).
69#
70#  First, for binutils-2.19:
71#
72#     fpu value           EABI tag          FPU reg count
73#    -----------------------------------------------------
74#       vfp                 VFPv2            16
75#       vfpv3               VFPv3-D16        32 (*)
76#       vfpv3-d16           VFPv3            16 (*)
77#       neon                VFPv3            32
78#
79#  And now for binutils-2.21
80#
81#     fpu value           EABI tag          FPU reg count
82#    -----------------------------------------------------
83#       vfp                 VFPv2            16
84#       vfpv3               VFPv3            32
85#       vfpv3-d16           VFPv3-D16        16
86#       neon                VFPv3            32
87#
88#  This shows that:
89#
90#    - The 'VFPv3' tag seems to match VFPv3-D32 exclusively on 2.21,
91#      but is a mess with 2.19
92#
93#    - Similarly, the 'vfpv3' value seems to match VFPv3-D32 as well,
94#      with the exception that binutils-2.19 is buggy and will put an
95#      invalid tag (VFPv3-D16, instead of VFPv3) in the generate ELF file.
96#
97#    - binutils 2.19 puts the wrong tag in the executable for vfpv3 and
98#      vfpv3-d16, then should probably be inverted!
99#
100#  The end result is that we can't use the EABI tag to determine the number
101#  of hardware FPU registers that are really used by the machine code with
102#  binutils 2.19 :-(
103#
104#  BONUS:
105#
106#    - When using 'neon', binutils-2.21 will also add a new tag named
107#      'Tag_Advanced_SIMD_arch' with value 'NEONv1'. Sadly, binutils-2.19
108#      doesn't do any of this.
109#
110
111# Check that an ELF binary is compatible with our armeabi-v7a ABI
112# (i.e. no NEON, and only 16 hardware registers being used).
113#
114# See technical note above to understand how this currently works.
115# We're still assuming the toolchain is built with the buggy binutils-2.19.
116#
117# $1: path to an ARMv7-A ELF binary (static lib, shared lib or executable)
118#
119check_armv7_elf_binary ()
120{
121    # We use a small awk script to parse the output of 'readelf -A'
122    # Which typically looks like:
123    #
124    # Attribute Section: aeabi
125    #   File Attributes
126    #   Tag_CPU_name: "7-A"
127    #   Tag_CPU_arch: v7
128    #   Tag_CPU_arch_profile: Application
129    #   Tag_ARM_ISA_use: Yes
130    #   Tag_THUMB_ISA_use: Thumb-2
131    #   Tag_VFP_arch: VFPv3-D16
132    #   Tag_ABI_PCS_wchar_t: 4
133    #   Tag_ABI_FP_denormal: Needed
134    #   Tag_ABI_FP_exceptions: Needed
135    #   Tag_ABI_FP_number_model: IEEE 754
136    #   Tag_ABI_align8_needed: Yes
137    #   Tag_ABI_align8_preserved: Yes, except leaf SP
138    #   Tag_ABI_enum_size: int
139    #   Tag_ABI_HardFP_use: SP and DP
140    #   Tag_ABI_optimization_goals: Aggressive Speed
141    #   Tag_unknown_44: 1 (0x1)
142    #
143    # Note that for static libraries, these sections will appear multiple
144    # time in the output of 'readelf -A'.
145
146    echo "Checking: $(basename $1)"
147    if [ ! -f "$1" ]; then
148        1>&2 echo "PANIC: Missing binary: $1"
149        exit 1
150    fi
151
152    # We want to check the values of Tag_CPU_name
153    CPU_NAMES=$(extract_arch_tag "$1" Tag_CPU_name)
154    VFP_ARCHS=$(extract_arch_tag "$1" Tag_VFP_arch)
155    NEON_ARCHS=$(extract_arch_tag "$1" Tag_Advanced_SIMD_arch)
156
157    # IMPORTANT NOTE: Even when using -march=armv7-a, the compiler may not
158    # necessarily use ARMv7-A specific instruction and will tag an object file
159    # with the following attributes:
160    #
161    # Attribute Section: aeabi
162    #   File Attributes
163    #   Tag_CPU_name: "5TE"
164    #   Tag_CPU_arch: v5TE
165    #   Tag_ARM_ISA_use: Yes
166    #   Tag_THUMB_ISA_use: Thumb-1
167    #   Tag_ABI_PCS_wchar_t: 4
168    #   Tag_ABI_FP_denormal: Needed
169    #   Tag_ABI_FP_exceptions: Needed
170    #   Tag_ABI_FP_number_model: IEEE 754
171    #   Tag_ABI_align8_needed: Yes
172    #   Tag_ABI_align8_preserved: Yes, except leaf SP
173    #   Tag_ABI_enum_size: int
174    #   Tag_ABI_optimization_goals: Aggressive Speed
175    #   Tag_unknown_44: 1 (0x1)
176    #
177    # This means that in static libraries, you can have both
178    # '5TE' and '7-A' CPU name tags at the same time, or only
179    # '5TE' or only '7-A', deal with all these cases properly.
180
181    echo "  found tags: CPU names:'$CPU_NAMES' VFP:'$VFP_ARCHS' NEON:'$NEON_ARCHS'"
182
183    # Clearly, any trace of NEON is a deal-breaker!
184    if [ "$NEON_ARCHS" ]; then
185        1>&2 echo "PANIC: Binary file should not contain NEON instructions: $1"
186        exit 1
187    fi
188
189    if is_static_file "$1"; then
190        # For static libraries / object files, it's ok to contain ARMv5TE binaries
191        if [ "$CPU_NAMES" == "\"5TE\"" -a "$CPU_NAMES" != "\"7-A\"" -a "$CPU_NAMES" != "\"5TE\" \"7-A\"" ]; then
192            # Neither ARMv7-A or ARMv5TE+ARMv7-A, something's fishy
193            1>&2 echo "PANIC: File is neither ARMv5TE or ARMv7-A binary: $1"
194            exit 1
195        fi
196
197        # exit here because some static libraries can have a mix of several
198        # VFP tags that make them difficult to check (e.g. libgnustl_static.a
199        # can have 'VFPv1 VFPv2 VFPv3' at the same time :-(
200        return
201    fi
202
203    # If we reach this point, we only contain ARMv7-A machine code, so look
204    # at the VFP arch tag(s)
205
206    # Sometimes no VFP_arch tag is placed in the final binary, this happens
207    # with libgabi++_shared.so for example, because the code doesn't have
208    # any floating point instructions.
209    #
210
211    # XXX: FOR NOW, ASSUME BROKEN binutils-2.19, AND THUS THAT 'VFPv3' IS VALID
212
213    if [ "$VFP_ARCHS" != "VFPv3" -a "$VFP_ARCHS" != "VFPv3-D16" -a "$VFP_ARCHS" != "" ]; then
214        1>&2 echo "PANIC: File is not a VFPv3-D16 binary: $1"
215        exit 1
216    fi
217}
218
219. $NDK/build/tools/dev-defaults.sh
220
221ARM_TOOLCHAIN_NAME=$(get_default_toolchain_name_for_arch arm)
222ARM_TOOLCHAIN_PREFIX=$(get_default_toolchain_prefix_for_arch arm)
223
224case $(uname -s) in
225    Darwin)
226      HOST_ARCH=`uname -m`
227      case "$HOST_ARCH" in
228          i?86) HOST_ARCH=x86
229              if ! echo __LP64__ | (CCOPTS= gcc -E - 2>/dev/null) | grep -q __LP64__ ; then
230                  HOST_ARCH=x86_64
231              fi
232              ;;
233      esac
234      HOST_TAG=darwin-$HOST_ARCH
235      ;;
236    Linux)
237      HOST_TAG=linux-$(uname -p)
238      ;;
239    *)
240      echo "WARNING: This test cannot run on this machine!" >&2
241      exit 0
242      ;;
243esac
244
245ARM_READELF=$NDK/toolchains/$ARM_TOOLCHAIN_NAME/prebuilt/$HOST_TAG/bin/${ARM_TOOLCHAIN_PREFIX}-readelf
246if [ ! -f "$ARM_READELF" ]; then
247    echo "ERROR: Missing binary: $ARM_READELF" >&2
248    exit 1
249fi
250
251ARMv7_ABIS="armeabi-v7a armeabi-v7a-hard"
252for ABI in $ARMv7_ABIS; do
253
254    GABIXX_LIBS=$NDK/sources/cxx-stl/gabi++/libs/$ABI
255    check_armv7_elf_binary $GABIXX_LIBS/libgabi++_shared.so
256    check_armv7_elf_binary $GABIXX_LIBS/libgabi++_static.a
257
258    STLPORT_LIBS=$NDK/sources/cxx-stl/stlport/libs/$ABI
259    check_armv7_elf_binary $STLPORT_LIBS/libstlport_shared.so
260    check_armv7_elf_binary $STLPORT_LIBS/libstlport_static.a
261
262
263    for VERSION in $DEFAULT_GCC_VERSION_LIST; do
264        GNUSTL_LIBS=$NDK/sources/cxx-stl/gnu-libstdc++/$VERSION/libs/$ABI
265        check_armv7_elf_binary $GNUSTL_LIBS/libsupc++.a
266        check_armv7_elf_binary $GNUSTL_LIBS/libgnustl_shared.so
267        check_armv7_elf_binary $GNUSTL_LIBS/libgnustl_static.a
268    done
269done
270
271echo "Done!"
272