Skip to content

Commit

Permalink
[ELF][ARM] Garbage collection support for .ARM.exidx sections
Browse files Browse the repository at this point in the history
.ARM.exidx sections have a reverse dependency on the section they have
a SHF_LINK_ORDER dependency on. In other words a .ARM.exidx section is
live only if the executable section it describes is live. We implement
this with a reverse dependency field in InputSection.

Adding the dependency to InputSection is the simplest implementation
but it could be moved out to a separate map if it were found to decrease
performance for non ARM targets.

Differential revision: https://reviews.llvm.org/D25234

llvm-svn: 283734
  • Loading branch information
smithp35 committed Oct 10, 2016
1 parent 50188b2 commit 0760605
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 0 deletions.
20 changes: 20 additions & 0 deletions lld/ELF/InputFiles.cpp
Expand Up @@ -145,6 +145,8 @@ void elf::ObjectFile<ELFT>::parse(DenseSet<StringRef> &ComdatGroups) {
// Read section and symbol tables.
initializeSections(ComdatGroups);
initializeSymbols();
if (Config->GcSections && Config->EMachine == EM_ARM )
initializeReverseDependencies();
}

// Sections with SHT_GROUP and comdat bits define comdat section groups.
Expand Down Expand Up @@ -270,6 +272,24 @@ void elf::ObjectFile<ELFT>::initializeSections(
}
}

// .ARM.exidx sections have a reverse dependency on the InputSection they
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
template <class ELFT>
void elf::ObjectFile<ELFT>::initializeReverseDependencies() {
unsigned I = -1;
for (const Elf_Shdr &Sec : this->ELFObj.sections()) {
++I;
if ((Sections[I] == &InputSection<ELFT>::Discarded) ||
!(Sec.sh_flags & SHF_LINK_ORDER))
continue;
if (Sec.sh_link >= Sections.size())
fatal(getFilename(this) + ": invalid sh_link index: " +
Twine(Sec.sh_link));
auto *IS = cast<InputSection<ELFT>>(Sections[Sec.sh_link]);
IS->DependentSection = Sections[I];
}
}

template <class ELFT>
InputSectionBase<ELFT> *
elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/InputFiles.h
Expand Up @@ -179,6 +179,7 @@ template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
private:
void initializeSections(llvm::DenseSet<StringRef> &ComdatGroups);
void initializeSymbols();
void initializeReverseDependencies();
InputSectionBase<ELFT> *getRelocTarget(const Elf_Shdr &Sec);
InputSectionBase<ELFT> *createInputSection(const Elf_Shdr &Sec);

Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/InputSection.h
Expand Up @@ -235,6 +235,9 @@ template <class ELFT> class InputSection : public InputSectionBase<ELFT> {
// to. The writer sets a value.
uint64_t OutSecOff = 0;

// InputSection that is dependent on us (reverse dependency for GC)
InputSectionBase<ELFT> *DependentSection = nullptr;

static bool classof(const InputSectionBase<ELFT> *S);

InputSectionBase<ELFT> *getRelocatedSection();
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/MarkLive.cpp
Expand Up @@ -91,6 +91,8 @@ static void forEachSuccessor(InputSection<ELFT> &Sec,
Fn(resolveReloc(Sec, Rel));
}
}
if (Sec.DependentSection)
Fn({Sec.DependentSection, 0});
}

// The .eh_frame section is an unfortunate special case.
Expand Down
123 changes: 123 additions & 0 deletions lld/test/ELF/arm-exidx-gc.s
@@ -0,0 +1,123 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
// RUN: ld.lld %t -o %t2 --gc-sections 2>&1
// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-EXIDX %s
// REQUIRES: arm

// Test the behavior of .ARM.exidx sections under garbage collection
// A .ARM.exidx section is live if it has a relocation to a live executable
// section.
// A .ARM.exidx section may have a relocation to a .ARM.extab section, if the
// .ARM.exidx is live then the .ARM.extab section is live

.syntax unified
.section .text.func1, "ax",%progbits
.global func1
func1:
.fnstart
bx lr
.save {r7, lr}
.setfp r7, sp, #0
.fnend

.section .text.unusedfunc1, "ax",%progbits
.global unusedfunc1
unusedfunc1:
.fnstart
bx lr
.cantunwind
.fnend

// Unwinding instructions for .text2 too large for an inline entry ARM.exidx
// entry. A separate .ARM.extab section is created to hold the unwind entries
// The .ARM.exidx table entry has a reference to the .ARM.extab section.
.section .text.func2, "ax",%progbits
.global func2
func2:
.fnstart
bx lr
.personality __gxx_personality_v0
.handlerdata
.section .text.func2
.fnend

// An unused function with a reference to a .ARM.extab section. Both should
// be removed by gc.
.section .text.unusedfunc2, "ax",%progbits
.global unusedfunc2
unusedfunc2:
.fnstart
bx lr
.personality __gxx_personality_v1
.handlerdata
.section .text.unusedfunc2
.fnend

// Dummy implementation of personality routines to satisfy reference from
// exception tables
.section .text.__gcc_personality_v0, "ax", %progbits
.global __gxx_personality_v0
__gxx_personality_v0:
.fnstart
bx lr
.cantunwind
.fnend

.section .text.__gcc_personality_v1, "ax", %progbits
.global __gxx_personality_v1
__gxx_personality_v1:
.fnstart
bx lr
.cantunwind
.fnend

.section .text.__aeabi_unwind_cpp_pr0, "ax", %progbits
.global __aeabi_unwind_cpp_pr0
__aeabi_unwind_cpp_pr0:
.fnstart
bx lr
.cantunwind
.fnend

// Entry point for GC
.text
.global _start
_start:
bl func1
bl func2
bx lr

// GC should have only removed unusedfunc1 and unusedfunc2 the personality
// routines are kept alive by references from live .ARM.exidx and .ARM.extab
// sections
// CHECK: Disassembly of section .text:
// CHECK-NEXT: _start:
// CHECK-NEXT: 11000: 01 00 00 eb bl #4 <func1>
// CHECK-NEXT: 11004: 01 00 00 eb bl #4 <func2>
// CHECK-NEXT: 11008: 1e ff 2f e1 bx lr
// CHECK: func1:
// CHECK-NEXT: 1100c: 1e ff 2f e1 bx lr
// CHECK: func2:
// CHECK-NEXT: 11010: 1e ff 2f e1 bx lr
// CHECK: __gxx_personality_v0:
// CHECK-NEXT: 11014: 1e ff 2f e1 bx lr
// CHECK: __aeabi_unwind_cpp_pr0:
// CHECK-NEXT: 11018: 1e ff 2f e1 bx lr

// GC should have removed table entries for unusedfunc1, unusedfunc2
// and __gxx_personality_v1
// CHECK-NOT: unusedfunc1
// CHECK-NOT: unusedfunc2
// CHECK-NOT: __gxx_personality_v1

// CHECK-EXIDX-NOT: Contents of section .ARM.extab.text.unusedfunc2:
// CHECK-EXIDX: Contents of section .ARM.exidx:
// 100d4 + f38 = 1100c = func1
// 100dc + f34 = 11010 = func2 (100e0 + 14 = 100f4 = .ARM.extab.text.func2)
// CHECK-EXIDX-NEXT: 100d4 380f0000 08849780 340f0000 14000000
// 100e4 + f30 = 11014 = __gxx_personality_v0
// 100ec + f2c = 11018 = __aeabi_unwind_cpp_pr0
// CHECK-EXIDX-NEXT: 100e4 300f0000 01000000 2c0f0000 01000000
// CHECK-EXIDX-NEXT: Contents of section .ARM.extab.text.func2:
// 100f4 + f20 = 11014 = __gxx_personality_v0
// CHECK-EXIDX-NEXT: 100f4 200f0000 b0b0b000

0 comments on commit 0760605

Please sign in to comment.