[lld] c9b1bd1 - [ELF] Support .rela.eh_frame with unordered r_offset values

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 29 08:51:19 PDT 2021


Author: Fangrui Song
Date: 2021-04-29T08:51:09-07:00
New Revision: c9b1bd10128956f51ea3b910b5d946a0ee7c2b0c

URL: https://github.com/llvm/llvm-project/commit/c9b1bd10128956f51ea3b910b5d946a0ee7c2b0c
DIFF: https://github.com/llvm/llvm-project/commit/c9b1bd10128956f51ea3b910b5d946a0ee7c2b0c.diff

LOG: [ELF] Support .rela.eh_frame with unordered r_offset values

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.

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D101116

Added: 
    lld/test/ELF/eh-frame-unordered-r_offset.s

Modified: 
    lld/ELF/InputSection.cpp
    lld/ELF/Relocations.cpp
    lld/ELF/Relocations.h

Removed: 
    


################################################################################
diff  --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 17fa7db15088a..143a183e35cd3 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -1326,6 +1326,11 @@ template <class ELFT> void EhInputSection::split() {
 
 template <class ELFT, class RelTy>
 void EhInputSection::split(ArrayRef<RelTy> rels) {
+  // getReloc expects the relocations to be sorted by r_offset. See the comment
+  // in scanRelocs.
+  SmallVector<RelTy, 0> storage;
+  rels = sortRels(rels, storage);
+
   unsigned relI = 0;
   for (size_t off = 0, end = data().size(); off != end;) {
     size_t size = readEhRecordSize(this, off);

diff  --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index d6615e1eaa275..bcb1fe012b659 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -1576,6 +1576,13 @@ static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
   if (config->emachine == EM_PPC64)
     checkPPC64TLSRelax<RelTy>(sec, rels);
 
+  // For EhInputSection, OffsetGetter expects the relocations to be sorted by
+  // r_offset. In rare cases (.eh_frame pieces are reordered by a linker
+  // script), the relocations may be unordered.
+  SmallVector<RelTy, 0> storage;
+  if (isa<EhInputSection>(sec))
+    rels = sortRels(rels, storage);
+
   for (auto i = rels.begin(), end = rels.end(); i != end;)
     scanReloc<ELFT>(sec, getOffset, i, rels.begin(), end);
 

diff  --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index dd0c9c385d121..a702aac183a91 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -196,6 +196,19 @@ template <class ELFT>
 static inline int64_t getAddend(const typename ELFT::Rela &rel) {
   return rel.r_addend;
 }
+
+template <typename RelTy>
+ArrayRef<RelTy> sortRels(ArrayRef<RelTy> rels, SmallVector<RelTy, 0> &storage) {
+  auto cmp = [](const RelTy &a, const RelTy &b) {
+    return a.r_offset < b.r_offset;
+  };
+  if (!llvm::is_sorted(rels, cmp)) {
+    storage.assign(rels.begin(), rels.end());
+    llvm::stable_sort(storage, cmp);
+    rels = storage;
+  }
+  return rels;
+}
 } // namespace elf
 } // namespace lld
 

diff  --git a/lld/test/ELF/eh-frame-unordered-r_offset.s b/lld/test/ELF/eh-frame-unordered-r_offset.s
new file mode 100644
index 0000000000000..7b5e4bc02cebe
--- /dev/null
+++ b/lld/test/ELF/eh-frame-unordered-r_offset.s
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+# RUN: split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/a.s -o %t/a.o
+# RUN: cp %t/a.o %t/b.o
+# RUN: ld.lld -r -T %t/lds %t/a.o %t/b.o -o %t/c.o
+# RUN: llvm-readelf -r %t/c.o | FileCheck %s --check-prefix=REL
+
+## If we swap two input .eh_frame, the r_offset values in relocations will be
+## unordered.
+# REL:          Offset
+# REL-NEXT: 0000000000000050
+# REL-NEXT: 0000000000000020
+
+## Test we can handle the rare case.
+# RUN: ld.lld %t/c.o -o %t/c
+# RUN: llvm-dwarfdump --eh-frame %t/c | FileCheck %s
+
+# CHECK: 00000000 00000014 00000000 CIE
+# CHECK: 00000018 00000014 0000001c FDE cie=00000000
+# CHECK: 00000030 00000014 00000034 FDE cie=00000000
+
+#--- a.s
+.cfi_startproc
+nop
+.cfi_endproc
+
+#--- lds
+SECTIONS {
+  .eh_frame : { *b.o(.eh_frame) *a.o(.eh_frame) }
+}


        


More information about the llvm-commits mailing list