[lld] f7bc79c - [lld-macho] Check if DWARF offset is too large for compact unwind

Jez Ng via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 4 06:30:34 PDT 2023


Author: Jez Ng
Date: 2023-04-04T09:30:22-04:00
New Revision: f7bc79c1c71f474f4690532f978384192c1f8d6c

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

LOG: [lld-macho] Check if DWARF offset is too large for compact unwind

For functions that use DWARF encodings, their compact unwind entry will
contain a hint about the offset of their DWARF entry from the start of
the `__eh_frame` section. The encoding only has 3 bytes to encode this
hint.

Previously, I neglected to check for overflow (and didn't realize that
the value was merely a hint without needing to be exact.) So for large
`__eh_frame` sections, the hint would overflow and cause the compact
unwind MODE flag to be corrupted, leading to uncaught exceptions at
runtime.

This diff fixes things by encoding zero as the hint for offsets that are
too large. The unwinder will start a linear search at the hint location
for the matching CFI record. The only requirement is that the hint
points to a valid CFI record start, and the start of the section is
always the start of a CFI record (in well-formed programs).

I'm not adding a test for this because generating the test inputs takes
a bit too much time. However, I have been testing locally with this lit
file, which takes about 15s to run on my machine:

```
# RUN: rm -rf %t; mkdir %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos11.0 %s -o %t/test.o
# RUN: %lld -dylib -lSystem %t/test.o -o %t/test

.subsections_via_symbols
.text
.p2align 2

_f:
  .cfi_startproc
.rept 0x7fffff
  .cfi_escape 0x2e, 0x10
.endr
  ret
  .cfi_endproc

_g:
  .cfi_startproc
  .cfi_escape 0x2e, 0x10
  ret
  .cfi_endproc
```

Reviewed By: #lld-macho, smeenai

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

Added: 
    

Modified: 
    lld/MachO/UnwindInfoSection.cpp

Removed: 
    


################################################################################
diff  --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp
index d480f7ed294a6..6820e177875ce 100644
--- a/lld/MachO/UnwindInfoSection.cpp
+++ b/lld/MachO/UnwindInfoSection.cpp
@@ -51,6 +51,13 @@ using namespace lld::macho;
 #define COMPRESSED_ENTRY_FUNC_OFFSET_MASK                                      \
   UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(~0)
 
+static_assert(static_cast<uint32_t>(UNWIND_X86_64_DWARF_SECTION_OFFSET) ==
+                  static_cast<uint32_t>(UNWIND_ARM64_DWARF_SECTION_OFFSET) &&
+              static_cast<uint32_t>(UNWIND_X86_64_DWARF_SECTION_OFFSET) ==
+                  static_cast<uint32_t>(UNWIND_X86_DWARF_SECTION_OFFSET));
+
+constexpr uint64_t DWARF_SECTION_OFFSET = UNWIND_X86_64_DWARF_SECTION_OFFSET;
+
 // Compact Unwind format is a Mach-O evolution of DWARF Unwind that
 // optimizes space and exception-time lookup.  Most DWARF unwind
 // entries can be replaced with Compact Unwind entries, but the ones
@@ -338,7 +345,19 @@ void UnwindInfoSectionImpl::relocateCompactUnwind(
 
     // If we have DWARF unwind info, create a CU entry that points to it.
     if (d->unwindEntry->getName() == section_names::ehFrame) {
-      cu.encoding = target->modeDwarfEncoding | d->unwindEntry->outSecOff;
+      // The unwinder will look for the DWARF entry starting at the hint,
+      // assuming the hint points to a valid CFI record start. If it
+      // fails to find the record, it proceeds in a linear search through the
+      // contiguous CFI records from the hint until the end of the section.
+      // Ideally, in the case where the offset is too large to be encoded, we
+      // would instead encode the largest possible offset to a valid CFI record,
+      // but since we don't keep track of that, just encode zero -- the start of
+      // the section is always the start of a CFI record.
+      uint64_t dwarfOffsetHint =
+          d->unwindEntry->outSecOff <= DWARF_SECTION_OFFSET
+              ? d->unwindEntry->outSecOff
+              : 0;
+      cu.encoding = target->modeDwarfEncoding | dwarfOffsetHint;
       const FDE &fde = cast<ObjFile>(d->getFile())->fdes[d->unwindEntry];
       cu.functionLength = fde.funcLength;
       cu.personality = fde.personality;


        


More information about the llvm-commits mailing list