In D64466 we permitted all of the relocation specifiers on any of MOV[NZK]. I think that this was the right thing to do for the unsigned relocation specifiers such as :abs_g*[_nc]:, however some research on how linker's such as ld.bfd and ld.gold handle the signed relocation specifiers makes me think that we should keep the restrictions on the signed specifiers such as :tprel_g*[_nc]:
This change adds back the distinction between movk and movnz, and permits the signed checking relocation specifiers on movz and non-checking on movk. The unsigned relocation specifiers can still be allowed on both. I've added tests for the signed relocation specifiers.
When the result of the operation is positive it doesn't matter whether the
sequence:
movz x0, #:abs_g3:foo movk x0, #:abs_g2_nc:foo movk x0, #:abs_g1_nc:foo movk x0, #:abs_g0_nc:foo
or
movz x0, #:abs_g0_nc:foo movk x0, #:abs_g1_nc:foo movk x0, #:abs_g2_nc:foo movk x0, #:abs_g3:foo
is used as the movz never needs to be transformed into a movn so all the relocations apply correctly on all AArch64 linkers.
When the result of the operation is signed a linker must transform the movz into a movn if the result is negative, and a movn to a movz if the result is positive.
For example:
movz x0, #:prel_g3:foo // Linker transforms to movn if result negative movk x0, #:prel_g2_nc:foo movk x0, #:prel_g1_nc:foo movk x0, #:prel_g0_nc:foo
The way the ABI is written with the checking forms only permitted on mov[nz] and the non-checking form on movk a linker doesn't need to
disassemble the binary to know that if the result of a checking relocation is negative, it needs to output a movn, and a movz otherwise. The ld.bfd and gold linkers have taken advantage of this and for the signed movw relocations such as R_AARCH64_TLSLE_TPREL_G2 it will write a movz if the result is positive and a movn otherwise, so if this relocation is applied to a movk (which the linker is not expecting) it will overwrite it with the wrong instruction. Similarly if R_AARCH64_TLSLE_TPREL_G0_NC is
applied to a mov[nz] (which the linker is not expecting) it will never change the instruction as it is expecting a movk.
It is true that if a linker disassembles the instruction to find out if it is a mov[zn] or movk and alter its behaviour dependent on the instruction then it doesn't matter whether the checked or non-checked relocation is applied (LLD will do this). However given that the signed case will break on ld.bfd and ld.gold I think we should be conservative in permitting the signed relocation specifiers.
We produce MOVK with PREL_G3 when the tagged-globals target feature is enabled, so we should allow this combination at least.