@dsillman,
64-bit ARM isn’t really the same thing as 64-bit x86 – it is a whole new instruction set architecture and exception model, which provides compatibility with the 32-bit ARM instruction set architecture(s) and exception model by allowing processors to be configured at different exception levels to run in the different execution states.
For Intel x86 and x86-64, you can use the same compiler as they have exactly the same instruction set, the same exception model, the difference is really in the extensions that bring it up to ‘64-bit’. For GCC you can pass -m32 or -m64 and generate code for either with the same toolchain, and it selects the 64-bit register set and a slightly different ABI.
For ARMv7 and AArch32 there are two instruction sets with only slightly different ABI, too – ARM and Thumb (or A32 and T32 in ARMv8 terms). You can switch between them on an “ARM” compiler using -mthumb and -marm. The registers are the same, but Thumb is a variable-length instruction set, either 16 or 32-bit opcodes, with the 16-bit opcodes having limited access. Switching between the A32 and T32 instruction sets at runtime is possible with “Branch and Exchange” (BX or BLX) instructions.
For Aarch64 and the A64 instruction set the only way to switch to the A32/T32 instruction sets (execution state) is to change exception level. That’s something a compiler simply cannot do (but it is what the VDSO code is doing in the code that’s broken - the SVC call will cause an exception from AArch32 User Mode (EL0) to AArch64 EL1.)
For ARMv8, building for AArch64 A64 or AArch32 A32/T32 you need two different compilers. The architecture version isn’t relevant in this case for the code we’re looking at – AArch32 state under ARMv8 is basically ARMv7 with the Virtualization and LPAE extensions, plus a few extra instructions. Building with an ARMv7 compiler is fine since the VDSO code has absolutely no use or access to these extensions (they’re only worthwhile at higher privilege levels, VDSO runs at User/EL0). You could actually even use an ARMv5T compiler or change the cc-flags line to -march=armv5t on your current compiler and it would build identical code. At the moment the VDSO code is built with -march=armv7-a so having a compiler capable of generating ARMv8 A32/T32 instructions – the very few that a compiler could actually generate for useful cases (dc zva for example, there’s no chance of a compiler knowing an AES encryption idiom and using aese/aesmc or the crc instructions) – has limited value, as it’s being restricted.
The key point is that GCC is configured at build-time to pre-select some options. That’s really how you get an “armv8l-linux-gnueabihf-” toolchain, it is preselected to build with “-marm -march=armv8-a+crc -mfpu=crypto-neon-fp-armv8 -mfloat-abi=hard” or something to that effect since that’s what Linaro have decided their target processor features for the default options are.
For Ubuntu toolchains (arm-linux-gnueabihf-) it’s much the same but they pick “-mthumb -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard” – the real reason is that it isn’t necessary to litter every package with those options, so that all packages are compatible with the lowest-common-denominator ARMv7 VFPv3 platform (which turns out to be nVidia Tegra and Tegra2, and a few Marvell SoCs :) and will build simply by using “gcc -c x.c -o x.o” with those options.
You can, still, build non-FPU soft-float linkage ARMv4 code with that toolchain (-marm -march=armv4 -msoft-float) but the defaults are the defaults.
Therefore you need to know which options are picked by default to override them and build the right code. Knowledge of the ABI in use (A32 and T32 are ever-so slightly different) is important when you stop using C code and start using assembly or inline assembly, and assuming that the compiler builds for the ARM/A32 ISA is poor on the part of whoever wrote the 32-bit VDSO code.
Ta,
Matt S.