This is an archive of the discontinued LLVM Phabricator instance.

[ELF] Support .rela.eh_frame with unordered r_offset values
ClosedPublic

Authored by MaskRay on Apr 22 2021, 3:29 PM.

Details

Summary

GNU ld -r can create .rela.eh_frame with unordered r_offset values.
(With LLD, we can craft such a case by reordering sections in .eh_frame.)
This is currently unsupported and will trigger
assert(pieces[i].inputOff <= off ... in OffsetGetter::get
(the content is corrupted in a -DLLVM_ENABLE_ASSERTIONS=off build).
This patch supports this case.

Diff Detail

Event Timeline

MaskRay created this revision.Apr 22 2021, 3:29 PM
MaskRay requested review of this revision.Apr 22 2021, 3:29 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 22 2021, 3:29 PM
MaskRay added inline comments.Apr 22 2021, 4:06 PM
lld/ELF/InputSection.cpp
1329

It is unfortunate that there are two pieces of code.
RelTy is parametric so we cannot add a member in EhInputSection caching the input relocations.

MaskRay edited the summary of this revision. (Show Details)Apr 22 2021, 5:20 PM

Can (and if so should - perhaps not) we avoid this patch by fixing the input in the first place? Under what circumstances can the relocations appear out of order?

lld/ELF/InputSection.cpp
1329

I might be missing something, but surely you could have some templated function that would allow you to avoid the code duplication at least?

1335

This sounds like this check could cost quite a bit in a large link. Have you done any performance comparisons of before and after of such a link? Given that the normal case is that things are sorted, I think we need to be somewhat careful here. Same goes below.

My understanding is that there is no requirement in ELF for relocations being in r_offset order even if that is how must object producers naturally generate them. This would make the inputs legal if unconventional. Although I have no numbers I'd expect a linear scan of the relocations for just EHInputSection with little computation to be small, but of course every linear scan costs. It may be possible to set a flag in EHInputSection after the first llvm::is_sorted so that only one test for the relocations being sorted is necessary.

One possible alternative, although I have little confidence it would be a better solution, is to effectively run something like a cut down scanRelocs() early for EHInputSections only. This would need to be done prior to split(). This could by construction ensure the relocations in EHInputSection were added in r_offset order and split() could use these relocations.

MaskRay marked an inline comment as done.Apr 23 2021, 10:35 AM

Can (and if so should - perhaps not) we avoid this patch by fixing the input in the first place? Under what circumstances can the relocations appear out of order?

GNU ld -r can create out of order .rela.eh_frame, even if I *don't* use a construct like .eh_frame : { *b.o(.eh_frame) *a.o(.eh_frame) }.

This sounds like this check could cost quite a bit in a large link.

I cannot observe any performance difference adding a linear scan.

lld/ELF/InputSection.cpp
1329

Not clear the trade-off is worthwhile. The template function needs to live in one header (our usage are in two .cpp files). On the other hand, there are only few lines and allocating a SmallVector and rels = sorted; cannot be dropped with a common function.

jhenderson added inline comments.Apr 26 2021, 1:37 AM
lld/ELF/InputSection.cpp
1329

The assignment can be folded in with the function call easily enough, I think. The advantage is that we don't have two places doing the same thing, potentially resulting in the code diverging in future patches. It also allows for future code reuse, and it seems not implausible we'll need this sorting elsewhere in the future.

template <typename RelTy>
ArrayRef<RelTy> sortRels(ArrayRef<RelTy> rels, ArrayRef<RelTy> storage) {
  auto cmp = [](const RelTy &a, const RelTy &b) {
    return a.r_offset < b.r_offset;
  };
  if (!llvm::is_sorted(rels, cmp)) {
    sorted.assign(rels.begin(), rels.end());
    llvm::stable_sort(storage, cmp);
    rels = sorted;
  }
  return rels;
}

template <class ELFT, class RelTy>
void EhInputSection::split(ArrayRef<RelTy> rels) {
  SmallVector<RelTy, 0> sorted;
  rels = sortRels(rels, sorted);
  ...
}
MaskRay updated this revision to Diff 341301.Apr 28 2021, 1:31 PM
MaskRay marked 2 inline comments as done.

add a template helper

This revision is now accepted and ready to land.Apr 29 2021, 12:24 AM
MaskRay edited the summary of this revision. (Show Details)Apr 29 2021, 8:48 AM
This revision was landed with ongoing or failed builds.Apr 29 2021, 8:51 AM
This revision was automatically updated to reflect the committed changes.