Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -110,7 +110,7 @@ const Elf_Shdr *getSectionHdr() const { return Header; } ObjectFile *getFile() const { return File; } uintX_t getOffset(const DefinedRegular &Sym) const; - + InputSectionBase *getLinkOrderDep() const; // Translate an offset in the input section to an offset in the output // section. uintX_t getOffset(uintX_t Offset) const; Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -125,6 +125,15 @@ return getOffset(Sym.Value); } +template +InputSectionBase* +InputSectionBase::getLinkOrderDep() const { + const Elf_Shdr *Hdr = getSectionHdr(); + if ((Hdr->sh_flags & SHF_LINK_ORDER) && Hdr->sh_link != 0) + return getFile()->getSections()[Hdr->sh_link]; + return nullptr; +} + template InputSection::InputSection(elf::ObjectFile *F, const Elf_Shdr *Header, StringRef Name) Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -878,6 +878,9 @@ template void OutputSection::finalize() { uint32_t Type = this->Header.sh_type; + // SHF_LINK_ORDER only has meaning in relocatable objects + if (!Config->Relocatable) + this->Header.sh_flags &= ~SHF_LINK_ORDER; if (Type != SHT_RELA && Type != SHT_REL) return; this->Header.sh_link = Out::SymTab->SectionIndex; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -26,6 +26,8 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; +using namespace llvm::support; +using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; @@ -1061,6 +1063,7 @@ Phdr TlsHdr(PT_TLS, PF_R); Phdr RelRo(PT_GNU_RELRO, PF_R); Phdr Note(PT_NOTE, PF_R); + Phdr ARMExidx(PT_ARM_EXIDX, PF_R); for (OutputSectionBase *Sec : OutputSections) { if (!(Sec->getFlags() & SHF_ALLOC)) break; @@ -1091,6 +1094,8 @@ RelRo.add(Sec); if (Sec->getType() == SHT_NOTE) Note.add(Sec); + if (Config->EMachine == EM_ARM && Sec->getType() == SHT_ARM_EXIDX) + ARMExidx.add(Sec); } // Add the TLS segment unless it's empty. @@ -1114,6 +1119,10 @@ Hdr.add(Out::EhFrameHdr); } + // PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME + if (ARMExidx.First) + Ret.push_back(std::move(ARMExidx)); + // PT_GNU_STACK is a special section to tell the loader to make the // pages for the stack non-executable. if (!Config->ZExecStack) { @@ -1398,6 +1407,61 @@ Sec->writeTo(Buf + Sec->getFileOff()); } +// Convert the .ARM.exidx table entries that use relative PREL31 offsets to +// Absolute addresses. This form is internal to LLD and is only used to +// make reordering the table simpler. +static void ARMExidxEntryPrelToAbs(uint8_t *Loc, uint64_t EntryVA) { + uint64_t Addr = Target->getImplicitAddend(Loc, R_ARM_PREL31) + EntryVA; + bool InlineEntry = + (read32le(Loc + 4) == 1 || (read32le(Loc + 4) & 0x80000000)); + if (InlineEntry) + // Set flag in unused bit of code address so that when we convert back we + // know which table entries to leave alone. + Addr |= 0x1; + else + write32le(Loc + 4, + Target->getImplicitAddend(Loc + 4, R_ARM_PREL31) + EntryVA + 4); + write32le(Loc, Addr); +} + +// Convert the .ARM.exidx table entries from the internal to LLD form using +// absolute addresses back to relative PREL31 offsets. +static void ARMExidxEntryAbsToPrel(uint8_t *Loc, uint64_t EntryVA) { + uint64_t Off = read32le(Loc) - EntryVA; + // ARMExidxEntryPreltoAbs sets bit 0 if the table entry has inline data + // that is not an address + bool InlineEntry = Off & 0x1; + Target->relocateOne(Loc, R_ARM_PREL31, Off & ~0x1); + if (!InlineEntry) + Target->relocateOne(Loc + 4, R_ARM_PREL31, + read32le(Loc + 4) - (EntryVA + 4)); +} + +// The table formed by the .ARM.exidx OutputSection has entries with two +// 4-byte fields: +// | PREL31 offset to function | Action to take for function | +// The table must be ordered in ascending virtual address of the functions +// identified by the first field of the table. Instead of using the +// SHF_LINK_ORDER dependency to reorder the sections prior to relocation we +// sort the table post-relocation. +// Ref: Exception handling ABI for the ARM architecture +static void sortARMExidx(uint8_t *Buf, uint64_t OutSecVA, uint64_t Size) { + struct ARMExidxEntry { + ulittle32_t Target; + ulittle32_t Action; + }; + ARMExidxEntry *Start = (ARMExidxEntry *)Buf; + size_t NumEnt = Size / sizeof(ARMExidxEntry); + for (uint64_t Off = 0; Off < Size; Off += 8) + ARMExidxEntryPrelToAbs(Buf + Off, OutSecVA + Off); + std::stable_sort(Start, Start + NumEnt, + [](const ARMExidxEntry &A, const ARMExidxEntry &B) { + return A.Target < B.Target; + }); + for (uint64_t Off = 0; Off < Size; Off += 8) + ARMExidxEntryAbsToPrel(Buf + Off, OutSecVA + Off); +} + // Write section contents to a mmap'ed file. template void Writer::writeSections() { uint8_t *Buf = Buffer->getBufferStart(); @@ -1414,6 +1478,11 @@ if (Sec != Out::Opd && Sec != Out::EhFrameHdr) Sec->writeTo(Buf + Sec->getFileOff()); + OutputSectionBase *ARMExidx = findSection(".ARM.exidx"); + if (!Config->Relocatable) + if (auto *OS = dyn_cast_or_null>(ARMExidx)) + sortARMExidx(Buf + OS->getFileOff(), OS->getVA(), OS->getSize()); + // The .eh_frame_hdr depends on .eh_frame section contents, therefore // it should be written after .eh_frame is written. if (!Out::EhFrame->empty() && Out::EhFrameHdr) Index: test/ELF/Inputs/arm-exidx-cantunwind.s =================================================================== --- /dev/null +++ test/ELF/Inputs/arm-exidx-cantunwind.s @@ -0,0 +1,40 @@ +// Functions that will generate a .ARM.exidx section with SHF_LINK_ORDER +// dependency on the progbits section containing the .cantunwind directive + .syntax unified + .section .func1, "ax",%progbits + .globl func1 +func1: + .fnstart + bx lr + .cantunwind + .fnend + + .section .func2, "ax", %progbits + .globl func2 +func2: + .fnstart + bx lr + .cantunwind + .fnend + + .section .func3, "ax",%progbits + .globl func3 +func3: + .fnstart + bx lr + .cantunwind + .fnend + + .section .text, "ax",%progbits + .globl func4 +func4: + .fnstart + bx lr + .cantunwind + .fnend + .globl func5 +func5: + .fnstart + bx lr + .cantunwind + .fnend Index: test/ELF/arm-exidx-canunwind.s =================================================================== --- /dev/null +++ test/ELF/arm-exidx-canunwind.s @@ -0,0 +1,96 @@ +// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t +// RUN: ld.lld %t -o %t2 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 +// RUN: llvm-readobj --program-headers --sections %t2 | FileCheck -check-prefix=CHECK-PT %s +// REQUIRES: arm + +// Test that inline unwinding table entries and references to .ARM.extab +// entries survive the re-ordering of the .ARM.exidx section + + .syntax unified + // Will produce an ARM.exidx entry with inline unwinding instructions + .section .text.func1, "ax",%progbits + .global func1 +func1: + .fnstart + bx lr + .save {r7, lr} + .setfp r7, sp, #0 + .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 + .long 0 + .section .text.func2 + .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: + bx lr + + .section .text.__aeabi_unwind_cpp_pr0, "ax", %progbits + .global __aeabi_unwind_cpp_pr0 +__aeabi_unwind_cpp_pr0: + bx lr + + .text + .global _start +_start: + bl func1 + bl func2 + bx lr + +// CHECK: Disassembly of section .text: +// CHECK-NEXT: _start: +// CHECK-NEXT: 11000: 01 00 00 eb bl #4 +// CHECK-NEXT: 11004: 01 00 00 eb bl #4 +// 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 + +// CHECK-EXIDX: Contents of section .ARM.exidx: +// 100d4 + f38 = 1100c = func1 (inline unwinding data) +// 100dc + f34 = 11010 = func2 (100e0 + 4 = 100e4 = .ARM.extab entry) +// CHECK-EXIDX-NEXT: 100d4 380f0000 08849780 340f0000 04000000 +// 100e4 + f30 = 11014 = __gxx_personality_v0 +// CHECK-EXIDX-NEXT: Contents of section .ARM.extab.text.func2: +// CHECK-EXIDX-NEXT: 100e4 300f0000 b0b0b000 00000000 + +// CHECK-PT: Name: .ARM.exidx +// CHECK-PT-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-PT-NEXT: Flags [ (0x2) +// CHECK-PT-NEXT: SHF_ALLOC (0x2) +// CHECK-PT-NEXT: ] +// CHECK-PT-NEXT: Address: 0x100D4 +// CHECK-PT-NEXT: Offset: 0xD4 +// CHECK-PT-NEXT: Size: 16 + +// CHECK-PT: Type: PT_ARM_EXIDX (0x70000001) +// CHECK-PT-NEXT: Offset: 0xD4 +// CHECK-PT-NEXT: VirtualAddress: 0x100D4 +// CHECK-PT-NEXT: PhysicalAddress: 0x100D4 +// CHECK-PT-NEXT: FileSize: 16 +// CHECK-PT-NEXT: MemSize: 16 +// CHECK-PT-NEXT: Flags [ (0x4) +// CHECK-PT-NEXT: PF_R (0x4) +// CHECK-PT-NEXT: ] +// CHECK-PT-NEXT: Alignment: 4 +// CHECK-PT-NEXT: } Index: test/ELF/arm-exidx-order.s =================================================================== --- /dev/null +++ test/ELF/arm-exidx-order.s @@ -0,0 +1,168 @@ +// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t +// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/arm-exidx-cantunwind.s -o %tcantunwind +// RUN: ld.lld %t %tcantunwind -o %t2 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 +// RUN: llvm-readobj --program-headers --sections %t2 | FileCheck -check-prefix=CHECK-PT %s +// Use Linker script to place .ARM.exidx in between .text and orphan sections +// RUN: echo "SECTIONS { \ +// RUN: .text 0x11000 : { *(.text*) } \ +// RUN: .ARM.exidx : { *(.ARM.exidx) } } " > %t.script +// RUN: ld.lld --script %t.script %tcantunwind %t -o %t3 2>&1 +// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-SCRIPT %s +// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-SCRIPT-EXIDX %s +// REQUIRES: arm + +// Each assembler created .ARM.exidx section has the SHF_LINK_ORDER flag set +// with the sh_link containing the section index of the executable section +// containing the function it describes. The linker must combine the .ARM.exidx +// InputSections in the same order that it has combined the executable section, +// such that the combined .ARM.exidx OutputSection can be used as a binary +// search table. + + .syntax unified + .section .text, "ax",%progbits + .globl _start +_start: + .fnstart + bx lr + .cantunwind + .fnend + + .section .text.f1, "ax", %progbits + .globl f1 +f1: + .fnstart + bx lr + .cantunwind + .fnend + + .section .text.f2, "ax", %progbits + .globl f2 +f2: + .fnstart + bx lr + .cantunwind + .fnend + .globl f3 +f3: + .fnstart + bx lr + .cantunwind + .fnend + +// Check default no linker script order. + +// CHECK: Disassembly of section .text: +// CHECK: _start: +// CHECK-NEXT: 11000: 1e ff 2f e1 bx lr +// CHECK: f1: +// CHECK-NEXT: 11004: 1e ff 2f e1 bx lr +// CHECK: f2: +// CHECK-NEXT: 11008: 1e ff 2f e1 bx lr +// CHECK: f3: +// CHECK-NEXT: 1100c: 1e ff 2f e1 bx lr +// CHECK: func4: +// CHECK-NEXT: 11010: 1e ff 2f e1 bx lr +// CHECK: func5: +// CHECK-NEXT: 11014: 1e ff 2f e1 bx lr +// CHECK: Disassembly of section .func1: +// CHECK-NEXT: func1: +// CHECK-NEXT: 11018: 1e ff 2f e1 bx lr +// CHECK: Disassembly of section .func2: +// CHECK-NEXT: func2: +// CHECK-NEXT: 1101c: 1e ff 2f e1 bx lr +// CHECK: Disassembly of section .func3: +// CHECK-NEXT: func3: +// CHECK-NEXT: 11020: 1e ff 2f e1 bx lr + +// Each .ARM.exidx section has two 4 byte fields +// Field 1 is the 31-bit offset to the function. The top bit is used to +// indicate whether Field 2 is a pointer or an inline table entry. +// Field 2 is either a pointer to a .ARM.extab section or an inline table +// In this example all Field 2 entries are inline can't unwind (0x1) +// We expect to see the entries in the same order as the functions + +// CHECK-EXIDX: Contents of section .ARM.exidx: +// 100d4 + f2c = 11000 = _start +// 100dc + f28 = 11004 = f1 +// CHECK-EXIDX-NEXT: 100d4 2c0f0000 01000000 280f0000 01000000 +// 100e4 + f24 = 11008 = f2 +// 100ec + f20 = 1100c = f3 +// CHECK-EXIDX-NEXT: 100e4 240f0000 01000000 200f0000 01000000 +// 100f4 + f1c = 11010 = func4 +// 100fc + f18 = 11014 = func5 +// CHECK-EXIDX-NEXT: 100f4 1c0f0000 01000000 180f0000 01000000 +// 10104 + f14 = 11018 = func1 +// 1010c + f10 = 1101c = func2 +// CHECK-EXIDX-NEXT: 10104 140f0000 01000000 100f0000 01000000 +// 10114 + f0c = 11020 = func3 +// CHECK-EXIDX-NEXT: 10114 0c0f0000 01000000 + +// Check that PT_ARM_EXIDX program header has been generated that describes +// the .ARM.exidx output section +// CHECK-PT: Name: .ARM.exidx +// CHECK-PT-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-PT-NEXT: Flags [ (0x2) +// CHECK-PT-NEXT: SHF_ALLOC (0x2) +// CHECK-PT-NEXT: ] +// CHECK-PT-NEXT: Address: 0x100D4 +// CHECK-PT-NEXT: Offset: 0xD4 +// CHECK-PT-NEXT: Size: 72 + +// CHECK-PT: Type: PT_ARM_EXIDX (0x70000001) +// CHECK-PT-NEXT: Offset: 0xD4 +// CHECK-PT-NEXT: VirtualAddress: 0x100D4 +// CHECK-PT-NEXT: PhysicalAddress: 0x100D4 +// CHECK-PT-NEXT: FileSize: 72 +// CHECK-PT-NEXT: MemSize: 72 +// CHECK-PT-NEXT: Flags [ (0x4) +// CHECK-PT-NEXT: PF_R (0x4) +// CHECK-PT-NEXT: ] +// CHECK-PT-NEXT: Alignment: 4 +// CHECK-PT-NEXT: } + + +// Check linker script order. The .ARM.exidx section will be inserted after +// the .text section but before the orphan sections + +// CHECK-SCRIPT: Disassembly of section .text: +// CHECK-SCRIPT-NEXT: func4: +// CHECK-SCRIPT-NEXT: 11000: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: func5: +// CHECK-SCRIPT-NEXT: 11004: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: _start: +// CHECK-SCRIPT-NEXT: 11008: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: f1: +// CHECK-SCRIPT-NEXT: 1100c: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: f2: +// CHECK-SCRIPT-NEXT: 11010: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: f3: +// CHECK-SCRIPT-NEXT: 11014: 1e ff 2f e1 bx lr +// CHECK-SCRIPT-NEXT: Disassembly of section .func1: +// CHECK-SCRIPT-NEXT: func1: +// CHECK-SCRIPT-NEXT: 11060: 1e ff 2f e1 bx lr +// CHECK-SCRIPT-NEXT: Disassembly of section .func2: +// CHECK-SCRIPT-NEXT: func2: +// CHECK-SCRIPT-NEXT: 11064: 1e ff 2f e1 bx lr +// CHECK-SCRIPT-NEXT: Disassembly of section .func3: +// CHECK-SCRIPT-NEXT: func3: +// CHECK-SCRIPT-NEXT: 11068: 1e ff 2f e1 bx lr + +// Check that the .ARM.exidx section is sorted in order as the functions +// The offset in field 1, is 32-bit so in the binary the most significant bit +// CHECK-SCRIPT-EXIDX: Contents of section .ARM.exidx: +// 11018 - 18 = 11000 func4 +// 11020 - 1c = 11004 func5 +// CHECK-SCRIPT-EXIDX-NEXT: 11018 e8ffff7f 01000000 e4ffff7f 01000000 +// 11028 - 20 = 11008 _start +// 11030 - 24 = 1100c f1 +// CHECK-SCRIPT-EXIDX-NEXT: 11028 e0ffff7f 01000000 dcffff7f 01000000 +// 11038 - 28 = 11010 f2 +// 11040 - 2c = 11014 f3 +// CHECK-SCRIPT-EXIDX-NEXT: 11038 d8ffff7f 01000000 d4ffff7f 01000000 +// 11048 + 18 = 11060 = func1 +// 11050 + 14 = 11064 = func2 +// CHECK-SCRIPT-EXIDX-NEXT: 11048 18000000 01000000 14000000 01000000 +// 11058 + 10 = 11068 = func3 +// CHECK-SCRIPT-EXIDX-NEXT: 11058 10000000 01000000 Index: test/ELF/arm-exidx-output.s =================================================================== --- test/ELF/arm-exidx-output.s +++ test/ELF/arm-exidx-output.s @@ -10,8 +10,6 @@ .syntax unified .section .text, "ax",%progbits .globl _start - .align 2 - .type _start,%function _start: .fnstart bx lr @@ -20,8 +18,6 @@ .section .text.f1, "ax", %progbits .globl f1 - .align 2 - .type f1,%function f1: .fnstart bx lr @@ -30,8 +26,6 @@ .section .text.f2, "ax", %progbits .globl f2 - .align 2 - .type f2,%function f2: .fnstart bx lr @@ -41,9 +35,8 @@ // CHECK: Section { // CHECK: Name: .ARM.exidx // CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001) -// CHECK-NEXT: Flags [ (0x82) +// CHECK-NEXT: Flags [ (0x2) // CHECK-NEXT: SHF_ALLOC (0x2) -// CHECK-NEXT: SHF_LINK_ORDER (0x80) // CHECK-NEXT: ] // CHECK-NOT: Name: .ARM.exidx.text.f1