Version 11.2-2022.02 of the GNU Toolchain contains a bug in the encoding of calls using blx instruction from thumb to arm instructions. When the size of the jump is exactly 16MB + 2, the jump is encoded as a jump of 2 instead of indirect jump. Next code reveals this error.
__attribute__((target("arm"))) int __attribute__ ((noinline)) f(); #define DIRECT_CALL \ asm ("NOP"); #define ERROR_CALL \ DIRECT_CALL \ asm ("NOP"); \ asm ("NOP"); \ asm ("NOP"); \ asm ("NOP"); #define INDIRECT_CALL \ ERROR_CALL \ asm ("NOP"); \ asm ("NOP"); \ asm ("NOP"); __attribute__((target("thumb"))) int main() { asm ("NOP"); asm ("NOP"); f(); //DIRECT_CALL ERROR_CALL //INDIRECT_CALL int k = f(); return k; } #define SIZE_DATA 0xfffe38 //char x[SIZE_DATA] __attribute__((section(".text"))); // fill in memory to make main() and f() almost 2^24 bytes apart. int x[SIZE_DATA >> 2] __attribute__((section(".text"))); // fill in memory to make main() and f() almost 2^24 bytes apart. __attribute__((target("arm"))) int __attribute__ ((noinline)) f() { x[(SIZE_DATA >> 2) - 1] = 12; static int i = 0; i++; asm ("NOP"); return i + x[(SIZE_DATA >> 2) - 1]; // make sure nothing is optimized }
To reproduce the error the example must be compiled using
arm-none-eabi-g++ -std=gnu++17 -mcpu=cortex-a9 -mfpu=vfpv3 -fdata-sections -ffunction-sections -mfloat-abi=hard -O3 -save-temps=obj -fverbose-asm --specs=nosys.specs $TEST_FILE
and the result must be disassembled using
arm-none-eabi-objdump -d a.out > a.s
In the resulting a.s the first call to function f() is encoded as:
8036: f000 e800 blx 8038 <main+0x8>
just a jump of offset 2 from address 8036. This is wrong.
The test has been compiled with AArch32 bare-metal target (arm-none-eabi)