Created attachment 13996 [details] Small source file for reproducing the bug We encountered a linking error in 11.1 and 11.2 of the ARM GNU Toolchain. There seems to be a bug in encodings of calls using blx when jumping from thumb to arm instructions. When the jump is exactly 2^24 + 2, the jump turns into a jump of 2 instead of an indirect jump. If the jump is shorter, a direct jump is generated correctly. If the jump is longer, an indirect jump is generated correctly. The bug can be reproduced for example on the ARM GNU Toolchain version 11.2-2022.02 for the AArch32 bare-metal target (arm-none-eabi) available for x86_64 Linux hosted cross toolchains here: [Arm GNU Toolchain | Arm GNU Toolchain Downloads – Arm Developer](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/downloads). A small example for triggering this bug for the above toolchain is attached as "test.cpp". For other versions of the toolchain, the example needs to be tweaked, because it is sensitive to code size, which typically differs between versions. The bug goes away if `ERROR_CALL` in "test.cpp" is replaced by either of the commented parts `//DIRECT_CALL` or `//INDIREC_CALL`. To reproduce this bug, the file "test.cpp" can be compiled with: 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.cpp Now if one disassembles the output using `arm-none-eabi-objdump -d a.out > a.s`, then the resulting file a.s contains the following encoding of the first call to f(): 8036: f000 e800 blx 8038 <main+0x8> This instruction represents a jump of two bytes forward to the address 8036 to 8038, which is wrong.
(In reply to Jori Bomanson from comment #0) Hi Jori, > A small example for triggering this bug for the above toolchain is attached > as "test.cpp". Would it be possible for you to create a test case using assembler source files, rather than C source files ? The output from gcc tends to be very sensitive to the version of the compiler being used, so attempts to reproduce the problem may not always work. (Especially if the reproducer does not wish to download and install the ARM GNU Toolchain). Also - have you tested this problem using the newly released 2.38 version of the binutils sources ? It may be possible that this bug has already been fixed... Cheers Nick
Created attachment 14019 [details] Binary object file that reveals the bug
Created attachment 14020 [details] Disassembled erroneous file after linking the object file
Created attachment 14021 [details] Disassembled correct file after using the fixed ld
The error occurs also in the master branch of binutils. The bug can be reproduced applying ld compiled for arm using the following configuration: export CPPFLAGS=-I/home/viorel/arm-none-eabi-original/buildNative/prerequisites/zlib-1.2.11/include -pipe export LDFLAGS=-L/home/viorel/arm-none-eabi-original/buildNative/prerequisites/zlib-1.2.11/lib /home/viorel/arm-none-eabi-original/sources/binutils-master/configure \ --build=x86_64-pc-linux-gnu \ --host=x86_64-pc-linux-gnu \ --with-python=yes \ --target=arm-none-eabi \ --prefix=/home/viorel/arm-none-eabi-original/installNative \ --docdir=/home/viorel/arm-none-eabi-original/installNative/share/doc \ --enable-lto \ --enable-gold \ --disable-werror CPPFLAGS=-UFORTIFY_SOURCE \ --disable-gdb \ --disable-sim \ --disable-libdecnumber \ --disable-readline \ --disable-nls \ --enable-plugins \ --with-system-zlib \ "--with-pkgversion=none-GCC-11.2.1-2022-03" This assumes that zlib is compiled appropriately. The binary object file (attached in zip format) must be linked using: arm-none-eabi-ld test-ld.o -o test-ld.bin And the result can be explored using: arm-none-eabi-objdump -d test-ld.bin > test-ld.s When using the unmodified master branch (22546800ad34a5ac6bc90e6701de3e74bad75551), the resulting file test-ld.s contains on line 9 the following assembly instruction: 8002: f000 e800 blx 8004 <main+0x4> This is obviously a wrong jump, as it is in the middle of the current instruction. The address 8004 is in the middle of current instruction "blx 8004". The corrected ld applied to the same object file results in: 8002: f000 e810 blx 8024 <___Z1fv_from_thumb> The bug is in the file bfd/elf32-arm.c at lines: #define THM_MAX_FWD_BRANCH_OFFSET ((1 << 22) -2 + 4) #define THM2_MAX_FWD_BRANCH_OFFSET (((1 << 24) - 2) + 4) These must be replaced by: #define THM_MAX_FWD_BRANCH_OFFSET ((1 << 22) -4 + 4) #define THM2_MAX_FWD_BRANCH_OFFSET (((1 << 24) - 4) + 4) Next there is a diff file with these changes as well as with some explanations. diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c index 616efe60..109c919d 100644 --- a/bfd/elf32-arm.c +++ b/bfd/elf32-arm.c @@ -2536,11 +2536,22 @@ static const bfd_vma elf32_arm_nacl_plt_entry [] = 0xea000000, /* b .Lplt_tail */ }; +/* There was a bug due to too high values of THM_MAX_FWD_BRANCH_OFFSET and + THM2_MAX_FWD_BRANCH_OFFSET. The first macro concerns the case when Thumb-2 is + not available, and secod macro when Thumb-2 is available. Among other things, they affect the range + of branches represented as blx instructions in Encoding T2 defined in Section + A8.8.25 of the ARM Architecture Reference Manual ARMv7-A and ARMv7-R + edition issue C.d. Such branches are specified there to have a maximum + forward offset that is a multiple of 4. Previously, the respective values + defined here were multiples of 2 but not 4 and they are included in comments + for reference. */ #define ARM_MAX_FWD_BRANCH_OFFSET ((((1 << 23) - 1) << 2) + 8) #define ARM_MAX_BWD_BRANCH_OFFSET ((-((1 << 23) << 2)) + 8) -#define THM_MAX_FWD_BRANCH_OFFSET ((1 << 22) -2 + 4) +#define THM_MAX_FWD_BRANCH_OFFSET ((1 << 22) - 4 + 4) +/* #define THM_MAX_FWD_BRANCH_OFFSET ((1 << 22) -2 + 4) */ #define THM_MAX_BWD_BRANCH_OFFSET (-(1 << 22) + 4) -#define THM2_MAX_FWD_BRANCH_OFFSET (((1 << 24) - 2) + 4) +#define THM2_MAX_FWD_BRANCH_OFFSET (((1 << 24) - 4) + 4) +/* #define THM2_MAX_FWD_BRANCH_OFFSET (((1 << 24) - 2) + 4) */ #define THM2_MAX_BWD_BRANCH_OFFSET (-(1 << 24) + 4) #define THM2_MAX_FWD_COND_BRANCH_OFFSET (((1 << 20) -2) + 4) #define THM2_MAX_BWD_COND_BRANCH_OFFSET (-(1 << 20) + 4)
The master branch has been updated by Nick Clifton <nickc@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=a747a286b9331adfba85a508beddaef47ea1a1c2 commit a747a286b9331adfba85a508beddaef47ea1a1c2 Author: Viorel Preoteasa <viorel.preoteasa@gmail.com> Date: Fri Mar 18 15:32:28 2022 +0000 Fix ld-arm bug in encoding of blx calls jumping from thumb to arm instructions PR 28924 * elf32-arm.c (THM_MAX_FWD_BRANCH_OFFSET): Fix definition. (THM2_MAX_FWD_BRANCH_OFFSET): Likewise.
Hi Viorel, Thanks for the patch - I have gone ahead and applied it. Cheers Nick