Skip to content

Commit

Permalink
[lld-macho] Emit LSDA info in compact unwind
Browse files Browse the repository at this point in the history
The LSDA pointers are encoded as offsets from the image base,
and arranged in one big contiguous array. Each second-level page records
the offset within that LSDA array which corresponds to the LSDA for its
first CU entry.

Reviewed By: clayborg

Differential Revision: https://reviews.llvm.org/D95810
  • Loading branch information
int3 committed Feb 8, 2021
1 parent 525bfa1 commit 5112035
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 12 deletions.
31 changes: 21 additions & 10 deletions lld/MachO/UnwindInfoSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,14 @@ void UnwindInfoSection::finalize() {
}
}

for (const CompactUnwindEntry64 *cu : cuPtrVector) {
uint32_t functionOffset = cu->functionAddress - in.header->addr;
functionToLsdaIndex[functionOffset] = lsdaEntries.size();
if (cu->lsda != 0)
lsdaEntries.push_back(
{functionOffset, static_cast<uint32_t>(cu->lsda - in.header->addr)});
}

// compute size of __TEXT,__unwind_info section
level2PagesOffset =
sizeof(unwind_info_section_header) +
Expand Down Expand Up @@ -386,29 +394,32 @@ void UnwindInfoSection::writeTo(uint8_t *buf) const {
uint64_t l2PagesOffset = level2PagesOffset;
auto *iep = reinterpret_cast<unwind_info_section_header_index_entry *>(i32p);
for (const SecondLevelPage &page : secondLevelPages) {
iep->functionOffset = cuPtrVector[page.entryIndex]->functionAddress;
iep->functionOffset =
cuPtrVector[page.entryIndex]->functionAddress - in.header->addr;
iep->secondLevelPagesSectionOffset = l2PagesOffset;
iep->lsdaIndexArraySectionOffset = lsdaOffset;
iep->lsdaIndexArraySectionOffset =
lsdaOffset + functionToLsdaIndex.lookup(iep->functionOffset) *
sizeof(unwind_info_section_header_lsda_index_entry);
iep++;
l2PagesOffset += SECOND_LEVEL_PAGE_BYTES;
}
// Level-1 sentinel
const CompactUnwindEntry64 &cuEnd = cuVector.back();
iep->functionOffset = cuEnd.functionAddress + cuEnd.functionLength;
iep->secondLevelPagesSectionOffset = 0;
iep->lsdaIndexArraySectionOffset = lsdaOffset;
iep->lsdaIndexArraySectionOffset =
lsdaOffset +
lsdaEntries.size() * sizeof(unwind_info_section_header_lsda_index_entry);
iep++;

// LSDAs
auto *lep =
reinterpret_cast<unwind_info_section_header_lsda_index_entry *>(iep);
for (const unwind_info_section_header_lsda_index_entry &lsda : lsdaEntries) {
lep->functionOffset = lsda.functionOffset;
lep->lsdaOffset = lsda.lsdaOffset;
}
size_t lsdaBytes =
lsdaEntries.size() * sizeof(unwind_info_section_header_lsda_index_entry);
memcpy(iep, lsdaEntries.data(), lsdaBytes);

// Level-2 pages
auto *pp = reinterpret_cast<uint32_t *>(lep);
auto *pp = reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(iep) +
lsdaBytes);
for (const SecondLevelPage &page : secondLevelPages) {
if (page.kind == UNWIND_SECOND_LEVEL_COMPRESSED) {
uintptr_t functionAddressBase =
Expand Down
3 changes: 3 additions & 0 deletions lld/MachO/UnwindInfoSection.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class UnwindInfoSection : public SyntheticSection {
// Indices of personality functions within the GOT.
std::vector<uint32_t> personalities;
std::vector<unwind_info_section_header_lsda_index_entry> lsdaEntries;
// Map of function offset (from the image base) to an index within the LSDA
// array.
llvm::DenseMap<uint32_t, uint32_t> functionToLsdaIndex;
std::vector<CompactUnwindEntry64> cuVector;
std::vector<CompactUnwindEntry64 *> cuPtrVector;
std::vector<SecondLevelPage> secondLevelPages;
Expand Down
22 changes: 20 additions & 2 deletions lld/test/MachO/compact-unwind.s
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin19.0.0 %s -o %t.o
# RUN: %lld -pie -lSystem -lc++ %t.o -o %t
# RUN: llvm-objdump --macho --unwind-info --indirect-symbols --rebase %t | FileCheck %s
# RUN: llvm-objdump --macho --unwind-info --syms --indirect-symbols --rebase %t | FileCheck %s

# CHECK: Indirect symbols for (__DATA_CONST,__got)
# CHECK-NEXT: address index name
# CHECK-DAG: 0x[[#%x,GXX_PERSONALITY:]] [[#]] ___gxx_personality_v0
# CHECK-DAG: 0x[[#%x,MY_PERSONALITY:]] LOCAL

# CHECK: SYMBOL TABLE:
# CHECK-DAG: [[#%x,MAIN:]] g F __TEXT,__text _main
# CHECK-DAG: [[#%x,FOO:]] g F __TEXT,__text _foo
# CHECK-DAG: [[#%x,EXCEPTION0:]] g O __TEXT,__gcc_except_tab _exception0
# CHECK-DAG: [[#%x,EXCEPTION1:]] g O __TEXT,__gcc_except_tab _exception1

# CHECK: Contents of __unwind_info section:
# CHECK: Personality functions: (count = 2)
# CHECK-NEXT: personality[1]: 0x{{0*}}[[#MY_PERSONALITY-0x100000000]]
# CHECK-NEXT: personality[2]: 0x{{0*}}[[#GXX_PERSONALITY-0x100000000]]
# CHECK: LSDA descriptors:
# CHECK-NEXT: [0]: function offset=0x{{0*}}[[#FOO-0x100000000]], LSDA offset=0x{{0*}}[[#EXCEPTION0-0x100000000]]
# CHECK-NEXT: [1]: function offset=0x{{0*}}[[#MAIN-0x100000000]], LSDA offset=0x{{0*}}[[#EXCEPTION1-0x100000000]]

## Check that we do not add rebase opcodes to the compact unwind section.
# CHECK: Rebase table:
Expand All @@ -20,12 +29,13 @@
# CHECK-NEXT: __DATA_CONST __got 0x{{[0-9a-f]*}} pointer
# CHECK-EMPTY:

.globl _main, _foo, _my_personality, _bar
.globl _main, _foo, _my_personality, _bar, _exception0, _exception1

.text
_foo:
.cfi_startproc
.cfi_personality 155, _my_personality
.cfi_lsda 16, _exception0
.cfi_def_cfa_offset 16
retq
.cfi_endproc
Expand All @@ -34,16 +44,24 @@ _bar:
.cfi_startproc
## Check that we dedup references to the same statically-linked personality.
.cfi_personality 155, _my_personality
.cfi_lsda 16, _exception0
.cfi_def_cfa_offset 16
retq
.cfi_endproc

_main:
.cfi_startproc
.cfi_personality 155, ___gxx_personality_v0
.cfi_lsda 16, _exception1
.cfi_def_cfa_offset 16
retq
.cfi_endproc

_my_personality:
retq

.section __TEXT,__gcc_except_tab
_exception0:
.space 1
_exception1:
.space 1

0 comments on commit 5112035

Please sign in to comment.