ARM-software / abi-aa

Application Binary Interface for the Arm® Architecture
Other
938 stars 188 forks source link

Are implicit addends for MOVW_UABS_G[123] and ADR_PREL_PG_HI21 shifted? #268

Closed statham-arm closed 2 months ago

statham-arm commented 3 months ago

Suppose I have an AArch64 ELF file containing:

For example:

mov  x0, #0x123          ; R_AARCH64_MOVW_UABS_G0_NC(mySymbol)
movk x0, #0x123, lsl #16 ; R_AARCH64_MOVW_UABS_G1_NC(mySymbol)
movk x0, #0x123, lsl #32 ; R_AARCH64_MOVW_UABS_G2_NC(mySymbol)
movk x0, #0x123, lsl #48 ; R_AARCH64_MOVW_UABS_G3(mySymbol)

What should the output be? There are two plausible interpretations:

For the analogous case in AAELF32, this is reasonably clear. §5.6.1.1 "Addends and PC-bias compensation" says (my emphasis):

For relocations processing MOVW and MOVT instructions (in both Arm and Thumb state), the initial addend is formed by interpreting the 16-bit literal field of the instruction as a 16-bit signed value in the range -32768 <= A < 32768. The interpretation is the same whether the relocated place contains a MOVW instruction or a MOVT instruction.

So in AAELF32, I'd expect that the analogous code would consistently compute mySymbol+0x123, and deliver the bottom and top 16 bits of that constant. This also seems sensible because that might plausibly be a thing I'd want. The other interpretation, of shifting each constant, would be useful if it allowed you to consistently relocate the whole instruction sequence to get mySymbol plus an arbitrary full-width constant, but it doesn't allow that, because the relocations are processed independently, so a carry when adding to the low word can't be accounted for in the high word. So the way AAELF32 has defined it, it's possible to add a small constant to a symbol and load the result via MOVW+MOVT, without having to resort to SHT_RELA to specify a larger addend.

But AAELF64 is not so clear. It says this about addends, in §5.7.2 "Addends and PC-bias" (again my emphasis):

If the relocation relocates an instruction the immediate field of the instruction is extracted, scaled as required by the instruction field encoding, and sign-extended to 64 bits.

It's clear that "scaled as required" should apply to things like branch offsets being interpreted as a multiple of 4 bytes. But it's not clear whether it also means you should scale up implicit addends in MOVK by 16, 32 or 48 bits, or in ADRP by 12 bits.

I think the most useful answer would be no: those addends should be taken to be applied unshifted to the symbol value, for the same reason as in AAELF32. But either way, I think the current wording is unclear.

smithp35 commented 3 months ago

I think we've got two possibilities.

Add special cases (like in the 32-bit ABI) for ADRP, MOV (wide immediate), MOVZ and MOVK instructions. I see that in the 32-bit ABI these clarifications were added in 2012, which is after the first release of the AArch64 ABI so it seems like we didn't consider the 64-bit ABI at the time, presumably as most implementations were using RELA.

The alternative is to interpret the following line strictly (https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst#572addends-and-pc-bias)

A RELA format relocation must be used if the initial addend cannot be encoded in the place.

And include addends for scaled immediates in ADRP and MOV (wide immediate), MOVZ and MOVK. An implementation would either need to use RELA relocations or always use a 0 addend and compensate with additional instructions.

I think the addition of a couple of special cases would make a fully SHT_REL implementation much more practical. Many existing open-source linkers cannot mix both REL and RELA relocations for the same section so converting individual relocations to RELA is not always practical.

statham-arm commented 3 months ago

Thanks. I've created #271 with some proposed wording.

statham-arm commented 2 months ago

271 is merged, so this issue can be closed too. The thing I wanted clarified is now clear.