This is an archive of the discontinued LLVM Phabricator instance.

[ARM] implement support for ALU/LDR PC-relative group relocations
ClosedPublic

Authored by ardb on Nov 18 2021, 9:57 AM.

Details

Summary

Currently, LLD does not support the complete set of ARM group relocations.
Given that I intend to start using these in the Linux kernel [0], let's add
support for these.

This implements the group processing as documented in the ELF psABI. Notably,
this means support is dropped for very far symbol references that also carry a
small component, where the immediate is rotated in such a way that only part of
it wraps to the other end of the 32-bit word. To me, it seems unlikely that
this is something anyone could be relying on, but of course I could be wrong.

[0] https://lore.kernel.org/r/20211122092816.2865873-8-ardb@kernel.org/

Diff Detail

Event Timeline

ardb created this revision.Nov 18 2021, 9:57 AM
ardb requested review of this revision.Nov 18 2021, 9:57 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 18 2021, 9:57 AM

At a glance this looks good to me. I've not gone through all the fine details of the calculation and the tests yet. I'll be out of office tomorrow so likely to be next week. Happy for someone else to approve in my absence. I agree with you that no-one should be relying on the wrap around case for an address calculation. The original code was taken from the ARM backend to make it easier to review as it had been used elsewhere in the code-base for many years.

ardb updated this revision to Diff 388457.Nov 19 2021, 4:08 AM
ardb retitled this revision from [ARM] implement support for ALU PC-relative group relocations to [ARM] implement support for ALU/LDR PC-relative group relocations.
ardb edited the summary of this revision. (Show Details)

Add support for relocations against LDR G1 and G2 as well, to complete the set.

peter.smith accepted this revision.Nov 22 2021, 9:39 AM

LGTM, thanks for working on this. I've marked approved from an Arm architecture perspective. Would also be good to wait a few days to see if @MaskRay is happy from a code-owner perspective.

For the benefit of other reviewers. A possible example of these relocations being used is https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst#73svr4-dso-like-plt-linkage in LLD we don't use the relocations directly and just hard code the positions.

The group relocations are defined in https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst#56relocation search for group relocations.

lld/ELF/Arch/ARM.cpp
429–439

Was about to comment that this was more like getLeadingZerosForGroup until seeing the reference parameter. One possibility is to return std::pair<rem, lz> and then use something like:

std::tie<imm, lz> = getRemAndLZForGroup(group, val);

In the main body.

483

IIUC I think the & 30 isn't strictly necessary as lz is always even when returned from getRemainderForGroup and lz + 8 can't be more than 30. No objections to keeping it in though.

This revision is now accepted and ready to land.Nov 22 2021, 9:39 AM
ardb added inline comments.Nov 22 2021, 9:52 AM
lld/ELF/Arch/ARM.cpp
429–439

Was about to comment that this was more like getLeadingZerosForGroup until seeing the reference parameter. One possibility is to return std::pair<rem, lz> and then use something like:

std::tie<imm, lz> = getRemAndLZForGroup(group, val);

In the main body.

Yeah, I wasn't too happy with this myself, but given that the LDR_PC_G1/2 relocations are always used in combination with ALU_PC_G0(1) to encode a single PC-relative symbol reference, I think it is important for readability that both code paths use the same helper to mask off the leading bits. And since the LDR version doesn't care about the shift, it only receives a single return value.

So if your suggested change is considered to be the best way to achieve this, I'm happy to adopt it.

483

IIUC I think the & 30 isn't strictly necessary as lz is always even when returned from getRemainderForGroup and lz + 8 can't be more than 30. No objections to keeping it in though.

I realized the same after posting the second revision. I'll drop it in the next one.

MaskRay accepted this revision.Nov 22 2021, 10:32 AM

If it's easy, would also be good to have an error test in arm-adr-err.s

lld/ELF/Arch/ARM.cpp
435
473
485
488
494
MaskRay added inline comments.Nov 22 2021, 10:34 AM
lld/ELF/Arch/ARM.cpp
479

Does it make sense to change all unsigned in this function and getRemainderForGroup to uint32_t?

https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/commit/?id=5d43c8cff9ed3edb37456fddc0ec2db77d219e7d

The page says "Notice: this object is not reachable from any branch." Since it is not in torvalds/master, I assume the link may be dangling in the future after your branch is force pushed.
Perhaps mention the subject so that it can be found for archaeology.

lld/ELF/Arch/ARM.cpp
435

val is uint32_t. Remove (uint64_t)?

ardb updated this revision to Diff 388987.Nov 22 2021, 11:54 AM

Implement feedback from Peter and Fangrui:

  • use std::pair to return two values
  • change types and U suffixes
  • add more tests for failures
MaskRay added inline comments.Nov 22 2021, 11:58 AM
lld/ELF/Arch/ARM.cpp
435

Is (uint64_t)0xffffff >> 32 possible? If lz can be 32, casting to (uint64_t) LGTM but it needs a comment. I am not familiar with the encoding and do not know whether lz can be 32.

437
ardb added inline comments.Nov 22 2021, 12:34 PM
lld/ELF/Arch/ARM.cpp
435

Yes, lz will be 32 if val == 0. This may happen when using multiple groups if the value can be fully encoded by the previous group(s).

Note that in that case, doing &= should not have any effect, but a 32-bit shift of a 32-bit type is better avoided entirely, I'd assume?

ardb updated this revision to Diff 389012.Nov 22 2021, 1:05 PM

Add stop condition to avoid 32-bit shift of 32-bit type

MaskRay added inline comments.Nov 22 2021, 1:23 PM
lld/ELF/Arch/ARM.cpp
429–439

https://en.cppreference.com/w/cpp/language/operator_arithmetic "In any case, if the value of the right operand is negative or is greater or equal to the number of bits in the promoted left operand, the behavior is undefined." C has the same UB. -fsanitize=undefined would error for the UB.

So your original (uint64_t) cast is correct. It just deserves a comment.

You can mark comments as "Done". Then when you upload a new revision like arc diff 'HEAD^', the comments will be marked as resolved. Some reviewers want to ensure all comments are resolved.

lld/test/ELF/arm-adr-err-long.s
4

Use split-file to avoid complex multi-line echo.

11

/// to make comments stand out from RUN and CHECK lines.

If you use # RUN:, the file can use ## for comments.

67

Delete trailing empty lines

ardb marked 14 inline comments as done.Nov 22 2021, 2:02 PM
ardb added inline comments.
lld/test/ELF/arm-adr-err-long.s
4

That code was taken verbatim from arm-adr-long.s so I see no need to deviate from it here.

ardb updated this revision to Diff 389038.Nov 22 2021, 2:03 PM

Use /// and remove trailing newlines

ardb updated this revision to Diff 389178.Nov 23 2021, 5:00 AM

For completeness, add support for R_ARM_LDRS_PC_Gn relocations as well. These are not as useful, given the small range of the unscaled addend, resulting in a guaranteed range of the entire group of only -/+ 16 MiB (24 bits).

Note that R_ARM_LDC_PC_Gn are still not supported, but these are even more obscure and so rarely used that I doubt whether anyone cares.

ardb edited the summary of this revision. (Show Details)Nov 23 2021, 5:14 AM
ardb marked an inline comment as done.Nov 23 2021, 12:06 PM
MaskRay added inline comments.Nov 23 2021, 12:46 PM
lld/ELF/Arch/ARM.cpp
435

Unaddressed: needs a uint64_t cast to fix UB. val &= (uint64_t)0xffffff >> lz; // lz may be 32

875

Call read32le(buf) once instead of three times.

ardb marked an inline comment as done.Nov 23 2021, 12:50 PM
ardb added inline comments.
lld/ELF/Arch/ARM.cpp
435

This is no longer true: lz == 32 only occurs if rem == 0x0, in which case we break out of the loop before.

MaskRay added inline comments.Nov 23 2021, 12:55 PM
lld/ELF/Arch/ARM.cpp
435

Thx. Would be nice to have the lz==32 test if not exist yet.

ardb updated this revision to Diff 389297.Nov 23 2021, 1:10 PM
ardb marked an inline comment as done.
  • avoid repeated read32le() calls
  • add comment to clarify how UB is avoided
MaskRay accepted this revision.Nov 24 2021, 9:31 AM