Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -173,7 +173,7 @@ void initializeSymbols(); InputSectionBase *getRelocTarget(const Elf_Shdr &Sec); InputSectionBase *createInputSection(const Elf_Shdr &Sec); - + void findRelocatableOrphanSections(); bool shouldMerge(const Elf_Shdr &Sec); SymbolBody *createSymbolBody(const Elf_Sym *Sym); Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -251,6 +251,44 @@ Sections[I] = createInputSection(Sec); } } + if (Config->Relocatable) + findRelocatableOrphanSections(); +} + +// Find and mark InputSections that we do not want to combine into another +// OutputSection when doing a relocatable link. +template +void elf::ObjectFile::findRelocatableOrphanSections() { + if (Config->EMachine != EM_ARM) + // Only RelocateOrphans defined so far are ARM specific + return; + unsigned I = -1; + const ELFFile &Obj = this->ELFObj; + for (const Elf_Shdr &Sec : Obj.sections()) { + ++I; + if (Sections[I] == &InputSection::Discarded) + continue; + StringRef Name = check(this->ELFObj.getSectionName(&Sec)); + switch (Sec.sh_type) { + case SHT_ARM_EXIDX: + if (Name.startswith(".ARM.exidx")) { + // Both the .ARM.exidx and the section it links to should not be + // combined. This preserves the SHF_LINK_ORDER property in the + // output object. + Sections[I]->RelocatableOrphan = 1; + if (Sec.sh_link != 0 && Sec.sh_link < Sections.size() && + Sections[Sec.sh_link] != &InputSection::Discarded) + Sections[Sec.sh_link]->RelocatableOrphan = 1; + } + break; + case SHT_REL: + // Do not combine relocation sections of RelocatableOrphans + InputSectionBase* target = getRelocTarget(Sec); + if (target->RelocatableOrphan) + Sections[I]->RelocatableOrphan = 1; + break; + } + } } template Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -45,6 +45,7 @@ InputSectionData(Kind SectionKind, StringRef Name, ArrayRef Data, bool Compressed, bool Live) : SectionKind(SectionKind), Live(Live), Compressed(Compressed), + RelocatableOrphan(0), Name(Name), Data(Data) {} private: @@ -58,6 +59,9 @@ unsigned Compressed : 1; + // Section should not be combined in relocatable link + unsigned RelocatableOrphan : 1; + uint32_t Alignment; StringRef Name; Index: ELF/MarkLive.cpp =================================================================== --- ELF/MarkLive.cpp +++ ELF/MarkLive.cpp @@ -185,7 +185,8 @@ return true; bool IsAllocSec = Sec->getSectionHdr()->sh_flags & SHF_ALLOC; - return !IsAllocSec || S.startswith(".ctors") || S.startswith(".dtors") || + return !IsAllocSec || S.startswith(".ARM.exidx") || + S.startswith(".ctors") || S.startswith(".dtors") || S.startswith(".init") || S.startswith(".fini") || S.startswith(".jcr"); } Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -876,8 +876,26 @@ this->Header.sh_entsize = sizeof(Elf_Rel); } +template +static InputSectionBase* +getLinkOrderDependency(const InputSectionBase* S) { + const typename ELFT::Shdr *Hdr = S->getSectionHdr(); + if ((Hdr->sh_flags & SHF_LINK_ORDER) && Hdr->sh_link != 0) + return S->getFile()->getSections()[Hdr->sh_link]; + return nullptr; +} + template void OutputSection::finalize() { uint32_t Type = this->Header.sh_type; + // When doing a relocatable link we must preserve the link order dependency + // of sections with the SHF_LINK_ORDER flag. The dependency is indicated by + // the sh_link field. We need to translate the InputSection sh_link to the + // OutputSection sh_link, all InputSections in the OutputSection have the + // same dependency. + if ((this->Header.sh_flags & SHF_LINK_ORDER) && !this->Sections.empty()) { + if (auto *D = getLinkOrderDependency(this->Sections.front())) + this->Header.sh_link = D->OutSec->SectionIndex; + } if (Type != SHT_RELA && Type != SHT_REL) return; this->Header.sh_link = Out::SymTab->SectionIndex; @@ -1851,13 +1869,16 @@ OutputSectionFactory::create(const SectionKey &Key, InputSectionBase *C) { uintX_t Flags = getOutFlags(C); + uint32_t Type = C->getSectionHdr()->sh_type; + if (C->RelocatableOrphan) + return {new OutputSection(C->Name, Type, Flags), true}; + OutputSectionBase *&Sec = Map[Key]; if (Sec) { Sec->updateFlags(Flags); return {Sec, false}; } - uint32_t Type = C->getSectionHdr()->sh_type; switch (C->kind()) { case InputSectionBase::Regular: Sec = new OutputSection(Key.Name, Type, Flags); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -25,6 +25,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; @@ -1048,6 +1050,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; @@ -1078,6 +1081,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. @@ -1101,6 +1106,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) { @@ -1380,6 +1389,62 @@ 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(); @@ -1396,6 +1461,12 @@ 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,50 @@ +// 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 + .align 2 + .type func1,%function +func1: + .fnstart + bx lr + .cantunwind + .fnend + + .section .func2, "ax", %progbits + .globl func2 + .align 2 + .type func2,%function +func2: + .fnstart + bx lr + .cantunwind + .fnend + + .section .func3, "ax",%progbits + .globl func3 + .align 2 + .type func3,%function +func3: + .fnstart + bx lr + .cantunwind + .fnend + + .section .text, "ax",%progbits + .globl func4 + .align 2 + .type func4,%function +func4: + .fnstart + bx lr + .cantunwind + .fnend + .globl func5 + .align 2 + .type func5,%function +func5: + .fnstart + bx lr + .cantunwind + .fnend Index: test/ELF/arm-exidx-canunwind.s =================================================================== --- /dev/null +++ test/ELF/arm-exidx-canunwind.s @@ -0,0 +1,131 @@ +// 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 + .align 2 + .global func1 + .type func1, %function +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 + .align 2 + .global func2 + .type func2, %function +func2: + .fnstart + .save {r7, lr} + .setfp r7, sp + bx lr + .personality __gxx_personality_v0 + .handlerdata + .align 2 + .byte 0xff + .byte 0x90 + .uleb128 0x16 + .byte 0x1 + .uleb128 0x32 + .uleb128 0 + .byte 0x1 + .byte 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 + .type __gxx_personality_v0, %function +__gxx_personality_v0: + .fnstart + bx lr + .cantunwind + .fnend + + .section .text.__aeabi_unwind_cpp_pr0, "ax", %progbits + .global __aeabi_unwind_cpp_pr0 + .type __aeabi_unwind_cpp_pr0, %function +__aeabi_unwind_cpp_pr0: + .fnstart + bx lr + .cantunwind + .fnend + + .text + .global _start + .type _start, %function +_start: + .fnstart + .cantunwind + bl func1 + bl func2 + bx lr + .fnend +// 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 + f2c = 11000 = _start (cantunwind) +// 100dc + f30 = 1100c = func1 (Inline unwind 80978408) +// CHECK-EXIDX-NEXT: 100d4 2c0f0000 01000000 300f0000 08849780 +// 100e4 + f2c = 11010 = func2 (EHT 100e8 + 14 = 100fc = extab entry for func2) +// 100ec + f28 = 11014 = __gxx_personality_v0 (cantunwind) +// CHECK-EXIDX-NEXT: 100e4 2c0f0000 14000000 280f0000 01000000 +// 100f4 + f24 = 11018 = __aeabi_unwind_cpp_pr0 (cantunwind) +// CHECK-EXIDX-NEXT: 100f4 240f0000 01000000 +// CHECK-EXIDX-NEXT: Contents of section .ARM.extab.text.func2: +// CHECK-EXIDX-NEXT: 100fc 180f0000 08849700 ff901601 32000100 + +// CHECK-PT: Name: .ARM.exidx +// CHECK-PT-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-PT-NEXT: Flags [ (0x82) +// CHECK-PT-NEXT: SHF_ALLOC (0x2) +// CHECK-PT-NEXT: SHF_LINK_ORDER (0x80) +// CHECK-PT-NEXT: ] +// CHECK-PT-NEXT: Address: 0x100D4 +// CHECK-PT-NEXT: Offset: 0xD4 +// CHECK-PT-NEXT: Size: 40 +// CHECK-PT-NEXT: Link: 3 +// CHECK-PT-NEXT: Info: 0 +// CHECK-PT-NEXT: AddressAlignment: 4 +// CHECK-PT-NEXT: EntrySize: 0 +// CHECK-PT-NEXT: } + +// 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: 40 +// CHECK-PT-NEXT: MemSize: 40 +// 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,182 @@ +// 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 + .align 2 + .type _start,%function +_start: + .fnstart + bx lr + .cantunwind + .fnend + + .section .text.f1, "ax", %progbits + .globl f1 + .align 2 + .type f1,%function +f1: + .fnstart + bx lr + .cantunwind + .fnend + + .section .text.f2, "ax", %progbits + .globl f2 + .align 2 + .type f2,%function +f2: + .fnstart + bx lr + .cantunwind + .fnend + .globl f3 + .align 2 + .type f3,%function +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 [ (0x82) +// CHECK-PT-NEXT: SHF_ALLOC (0x2) +// CHECK-PT-NEXT: SHF_LINK_ORDER (0x80) +// CHECK-PT-NEXT: ] +// CHECK-PT-NEXT: Address: 0x100D4 +// CHECK-PT-NEXT: Offset: 0xD4 +// CHECK-PT-NEXT: Size: 72 +// CHECK-PT-NEXT: Link: 2 +// CHECK-PT-NEXT: Info: 0 +// CHECK-PT-NEXT: AddressAlignment: 4 +// CHECK-PT-NEXT: EntrySize: 0 +// CHECK-PT-NEXT: } + +// 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-relocatable.s =================================================================== --- /dev/null +++ test/ELF/arm-exidx-relocatable.s @@ -0,0 +1,165 @@ +// 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 +// Check that relocatable link maintains SHF_LINK_ORDER +// RUN: ld.lld -r %t %tcantunwind -o %t4 2>&1 +// RUN: llvm-readobj -s %t4 | FileCheck %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. To maintain this property in +// relocatable links we pass through the .ARM.exidx section, the section it +// it has a sh_link to, and the associated relocation sections uncombined. + + .syntax unified + .section .text, "ax",%progbits + .globl _start + .align 2 + .type _start,%function +_start: + .fnstart + bx lr + .cantunwind + .fnend + + .section .text.f1, "ax", %progbits + .globl f1 + .align 2 + .type f1,%function +f1: + .fnstart + bx lr + .cantunwind + .fnend + + .section .text.f2, "ax", %progbits + .globl f2 + .align 2 + .type f2,%function +f2: + .fnstart + bx lr + .cantunwind + .fnend + .globl f3 + .align 2 + .type f3,%function +f3: + .fnstart + bx lr + .cantunwind + .fnend + + +// CHECK: Name: .ARM.exidx +// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-NEXT: Flags [ (0x82) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_LINK_ORDER (0x80) +// CHECK: Size: 8 +// CHECK-NEXT: Link: 8 +// CHECK: Name: .ARM.exidx.text.f1 +// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-NEXT: Flags [ (0x82) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_LINK_ORDER (0x80) +// CHECK: Size: 8 +// CHECK-NEXT: Link: 9 +// CHECK: Name: .ARM.exidx.text.f2 +// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-NEXT: Flags [ (0x82) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_LINK_ORDER (0x80) +// CHECK: Size: 16 +// CHECK-NEXT: Link: 10 +// CHECK: Name: .ARM.exidx.func1 +// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-NEXT: Flags [ (0x82) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_LINK_ORDER (0x80) +// CHECK: Size: 8 +// CHECK-NEXT: Link: 12 +// CHECK: Name: .ARM.exidx.func2 +// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-NEXT: Flags [ (0x82) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_LINK_ORDER (0x80) +// CHECK: Size: 8 +// CHECK-NEXT: Link: 13 +// CHECK: Name: .ARM.exidx.func3 +// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-NEXT: Flags [ (0x82) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_LINK_ORDER (0x80) +// CHECK: Size: 8 +// CHECK-NEXT: Link: 14 +// CHECK: Name: .ARM.exidx +// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +// CHECK-NEXT: Flags [ (0x82) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_LINK_ORDER (0x80) +// CHECK: Size: 16 +// CHECK-NEXT: Link: 11 +// CHECK: Name: .text +// CHECK-NEXT: Type: SHT_PROGBITS (0x1) +// CHECK-NEXT: Flags [ (0x6) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_EXECINSTR (0x4) +// CHECK: Size: 4 +// CHECK: Name: .text.f1 +// CHECK-NEXT: Type: SHT_PROGBITS (0x1) +// CHECK-NEXT: Flags [ (0x6) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_EXECINSTR (0x4) +// CHECK: Size: 4 +// CHECK: Name: .text.f2 +// CHECK-NEXT: Type: SHT_PROGBITS (0x1) +// CHECK-NEXT: Flags [ (0x6) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_EXECINSTR (0x4) +// CHECK: Size: 8 +// CHECK: Name: .text +// CHECK-NEXT: Type: SHT_PROGBITS (0x1) +// CHECK-NEXT: Flags [ (0x6) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_EXECINSTR (0x4) +// CHECK: Size: 8 +// CHECK: Name: .func1 +// CHECK-NEXT: Type: SHT_PROGBITS (0x1) +// CHECK-NEXT: Flags [ (0x6) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_EXECINSTR (0x4) +// CHECK: Size: 4 +// CHECK: Name: .func2 +// CHECK-NEXT: Type: SHT_PROGBITS (0x1) +// CHECK-NEXT: Flags [ (0x6) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_EXECINSTR (0x4) +// CHECK: Size: 4 +// CHECK: Name: .func3 +// CHECK-NEXT: Type: SHT_PROGBITS (0x1) +// CHECK-NEXT: Flags [ (0x6) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_EXECINSTR (0x4) +// CHECK: Size: 4 +// CHECK: Name: .rel.ARM.exidx +// CHECK-NEXT: Type: SHT_REL (0x9) +// CHECK: Info: 1 +// CHECK: Name: .rel.ARM.exidx.text.f1 +// CHECK-NEXT: Type: SHT_REL (0x9) +// CHECK: Info: 2 +// CHECK: Name: .rel.ARM.exidx.text.f2 +// CHECK-NEXT: Type: SHT_REL (0x9) +// CHECK: Info: 3 +// CHECK: Name: .rel.ARM.exidx.func1 +// CHECK-NEXT: Type: SHT_REL (0x9) +// CHECK: Info: 4 +// CHECK: Name: .rel.ARM.exidx.func2 +// CHECK-NEXT: Type: SHT_REL (0x9) +// CHECK: Info: 5 +// CHECK: Name: .rel.ARM.exidx.func3 +// CHECK-NEXT: Type: SHT_REL (0x9) +// CHECK: Info: 6 +// CHECK: Name: .rel.ARM.exidx (146) +// CHECK-NEXT: Type: SHT_REL (0x9) +// CHECK: Info: 7