Skip to content

Commit

Permalink
[ELF] --gc-sections: collect unused .gcc_except_table in section grou…
Browse files Browse the repository at this point in the history
…ps and associated text sections

`try ... catch` in an inline function produces `.gcc_except_table.*` in a COMDAT
group with GCC or newer Clang (since D83655). For --gc-sections, currently we
scan `.eh_frame` pieces and mark liveness of such a `.gcc_except_table.*` and
then the associated `.text.*` (if a member in a section group is retained, the
others should be retained as well).

Essentially all `.text.*` and `.gcc_except_table.*` compiled from inline
functions with `try ... catch` cannot be discarded by the imprecise
--gc-sections.  Compared with the state before D83655, the output
`.gcc_except_table` is smaller (non-prevailing copies in COMDAT groups can now
be discarded) but `.text` may be larger, i.e. size regression.

This patch teaches the .eh_frame piece scanning code to not mark
`.gcc_except_table` in a section group, thus allow unused `.text.*` and
`.gcc_except_table.*` in a section group to be discarded.

Note, non-group `.gcc_except_table` can still not be discarded. That is the status quo.

Reviewed By: grimar, echristo

Differential Revision: https://reviews.llvm.org/D91579
  • Loading branch information
MaskRay committed Nov 17, 2020
1 parent f4d9d80 commit 3f90918
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 27 deletions.
18 changes: 12 additions & 6 deletions lld/ELF/MarkLive.cpp
Expand Up @@ -56,7 +56,7 @@ template <class ELFT> class MarkLive {
void mark();

template <class RelTy>
void resolveReloc(InputSectionBase &sec, RelTy &rel, bool isLSDA);
void resolveReloc(InputSectionBase &sec, RelTy &rel, bool fromFDE);

template <class RelTy>
void scanEhFrameSection(EhInputSection &eh, ArrayRef<RelTy> rels);
Expand Down Expand Up @@ -89,7 +89,7 @@ static uint64_t getAddend(InputSectionBase &sec,
template <class ELFT>
template <class RelTy>
void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
bool isLSDA) {
bool fromFDE) {
Symbol &sym = sec.getFile<ELFT>()->getRelocTargetSym(rel);

// If a symbol is referenced in a live section, it is used.
Expand All @@ -104,7 +104,16 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
if (d->isSection())
offset += getAddend<ELFT>(sec, rel);

if (!isLSDA || !(relSec->flags & SHF_EXECINSTR))
// fromFDE being true means this is referenced by a FDE in a .eh_frame
// piece. The relocation points to the described function or to a LSDA. We
// only need to keep the LSDA live, so ignore anything that points to
// executable sections. If the LSDA is in a section group, we ignore the
// relocation as well because (a) if the associated text section is live,
// the LSDA will be retained due to section group rules (b) if the
// associated text section should be discarded, marking the LSDA will
// unnecessarily retain the text section.
if (!(fromFDE &&
((relSec->flags & SHF_EXECINSTR) || relSec->nextInSectionGroup)))
enqueue(relSec, offset);
return;
}
Expand Down Expand Up @@ -148,9 +157,6 @@ void MarkLive<ELFT>::scanEhFrameSection(EhInputSection &eh,
continue;
}

// This is a FDE. The relocations point to the described function or to
// a LSDA. We only need to keep the LSDA alive, so ignore anything that
// points to executable sections.
uint64_t pieceEnd = piece.inputOff + piece.size;
for (size_t j = firstRelI, end2 = rels.size();
j < end2 && rels[j].r_offset < pieceEnd; ++j)
Expand Down
69 changes: 48 additions & 21 deletions lld/test/ELF/gc-sections-lsda.s
@@ -1,21 +1,48 @@
// REQUIRES: x86

// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
// RUN: ld.lld -shared --gc-sections %t.o -o %t

// Test that we handle .eh_frame keeping sections alive. We could be more
// precise and gc the entire contents of this file, but test that at least
// we are consistent: if we keep .abc, we have to keep .foo

// RUN: llvm-readobj -S %t | FileCheck %s
// CHECK: Name: .abc
// CHECK: Name: .foo

.cfi_startproc
.cfi_lsda 0x1b,zed
.cfi_endproc
.section .abc,"a"
zed:
.long bar-.
.section .foo,"ax"
bar:
# REQUIRES: x86
# RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux

## Discard an unused .gcc_except_table in a COMDAT group if the associated text
## section is discarded.

# RUN: ld.lld --gc-sections --print-gc-sections -u _Z3foov %t.o -o /dev/null | \
# RUN: FileCheck %s --implicit-check-not=.gcc_except_table

# CHECK: removing unused section {{.*}}.o:(.text._Z6comdatv)
# CHECK-NEXT: removing unused section {{.*}}.o:(.gcc_except_table._Z6comdatv)

## An unused non-group .gcc_except_table is not discarded.

# RUN: ld.lld --gc-sections --print-gc-sections -u _Z6comdatv %t.o -o /dev/null | \
# RUN: FileCheck /dev/null --implicit-check-not=.gcc_except_table

## If the text sections are live, the .gcc_except_table sections are retained as
## well because they are referenced by .eh_frame pieces.

# RUN: ld.lld --gc-sections --print-gc-sections -u _Z3foov -u _Z6comdatv %t.o -o /dev/null | \
# RUN: FileCheck %s --check-prefix=KEEP

# KEEP-NOT: .gcc_except_table

.section .text._Z3foov,"ax",@progbits
.globl _Z3foov
_Z3foov:
.cfi_startproc
ret
.cfi_lsda 0x1b,.Lexception0
.cfi_endproc

.section .text._Z6comdatv,"axG",@progbits,_Z6comdatv,comdat
.globl _Z6comdatv
_Z6comdatv:
.cfi_startproc
ret
.cfi_lsda 0x1b,.Lexception1
.cfi_endproc

.section .gcc_except_table._Z3foov,"a",@progbits
.Lexception0:
.byte 255

.section .gcc_except_table._Z6comdatv,"aG",@progbits,_Z6comdatv,comdat
.Lexception1:
.byte 255

0 comments on commit 3f90918

Please sign in to comment.