ARM Thumb relocation fails to handle sign of veneer jumps
While working on http://hackage.haskell.org/trac/ghc/ticket/7794 on Linux/ARM (ARMv7, hard float), I found that 'inplace/bin/ghc-stage2 --interactive' was raising various signals along the lines of SIGSEGV/SIGILL on startup. Considerable hair-pulling and gdb later, I found that this was a jump into space when it should have been jumping to nl_langinfo from localeEncoding in base. After even more poking around with gdb, I've tracked this down to a linker bug and have a patch.
When the linker processes an R_ARM_THM_CALL or R_ARM_THM_JUMP24 relocation, it sometimes needs to generate a "veneer" jump target if the true destination is out of range. This is done almost entirely correctly. However, it reuses the sign bit decoded from the original instruction when re-encoding. This means that if the veneer is on the opposite side of the branch instruction from the unrelocated target, the branch target will be encoded incorrectly. This is a common case since the veneer is typically generated after the code being relocated but a to-be-relocated R_ARM_THM_CALL instruction will typically be a branch to PC-4 (a.k.a. the branch source itself).
The simple, and I hope obvious, fix is to compute the sign from the new offset. The attached patch should apply to both master and ghc-7.6.