Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -260,6 +260,10 @@ S->RelocSection = &Sec; break; } + if (auto *S = dyn_cast>(Target)) { + S->RelocSections.push_back(&Sec); + break; + } fatal(getFilename(this) + ": relocations pointing to SHF_MERGE are not supported"); } @@ -280,6 +284,15 @@ MipsAbiFlags.reset(new MipsAbiFlagsInputSection(this, &Sec)); Sections[I] = MipsAbiFlags.get(); break; + case SHT_ARM_EXIDX: + if (Config->EMachine == EM_ARM) { + // SHT_ARM_EXIDX is shared with SHT_X86_64_UNWIND + // which is handled by the default case + Sections[I] = + new (IAlloc.Allocate()) ARMExidxInputSection(this, &Sec); + break; + } + // fallthrough default: Sections[I] = createInputSection(Sec); } Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -38,7 +38,9 @@ // section class InputSectionData { public: - enum Kind { Regular, EHFrame, Merge, MipsReginfo, MipsOptions, MipsAbiFlags }; + enum Kind { + Regular, EHFrame, Merge, MipsReginfo, MipsOptions, MipsAbiFlags, ARMExidx + }; // The garbage collector sets sections' Live bits. // If GC is disabled, all sections are considered live by default. @@ -284,6 +286,33 @@ const llvm::object::Elf_Mips_ABIFlags *Flags = nullptr; }; +template +class ARMExidxInputSection : public InputSectionBase { + typedef typename ELFT::Shdr Elf_Shdr; + +public: + // The offset from beginning of the output sections this section was assigned + // to. The writer sets a value. + uint64_t OutSecOff = 0; + + ARMExidxInputSection(ObjectFile *F, const Elf_Shdr *Hdr); + static bool classof(const InputSectionBase *S); + // Relocation sections that refer to this one. + llvm::TinyPtrVector RelocSections; + + // .ARM.exidx sections have a SHF_LINK_ORDER dependency with respect + // to an InputSection containing executable code + InputSection *getLinkedToSection() const; + + // In a final link this is .ARM.exidx . In a relocatable link the name of the + // section is derived from the linked-to section. + StringRef getOutputSectionName() const; + + // Write this section to a mmap'ed file, assuming Buf is pointing to + // beginning of the output section. + void writeTo(uint8_t *Buf); +}; + // Common symbols don't belong to any section. But it is easier for us // to handle them as if they belong to some input section. So we defined // this class. CommonInputSection is a virtual singleton class that Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -86,6 +86,8 @@ fatal(getName(this) + ": unsupported reference to the middle of '" + getSectionName() + "' section"); return this->OutSec->getVA(); + case ARMExidx: + return cast>(this)->OutSecOff + Offset; } llvm_unreachable("invalid section kind"); } @@ -694,6 +696,63 @@ } template +ARMExidxInputSection::ARMExidxInputSection(ObjectFile *F, + const Elf_Shdr *Hdr) + : InputSectionBase(F, Hdr, InputSectionBase::ARMExidx) { + // Mark .ARM.exidx sections as live by default because there are + // usually no relocations that point to .ARM.exidx sections. Otherwise, + // the garbage collector would drop all .ARM.exidx sections. + this->Live = true; + // .ARM.exidx sections must have a SHF_LINK_ORDER dependency with respect + // to an executable Section + if (!(Hdr->sh_flags & SHF_LINK_ORDER) || Hdr->sh_link == 0) + fatal(getName(this) + ": unsupported link order reference"); +} + +template +bool ARMExidxInputSection::classof(const InputSectionBase *S) { + return S->SectionKind == InputSectionBase::ARMExidx; +} + +template +InputSection *ARMExidxInputSection::getLinkedToSection() const { + ArrayRef *> Sections = this->File->getSections(); + InputSectionBase *LT = Sections[this->Header->sh_link]; + auto *LinkedTo = dyn_cast>(LT); + if (LinkedTo == &InputSection::Discarded) + return nullptr; + if (!LinkedTo) + fatal(getName(this) + ": unsupported link order reference"); + return LinkedTo; +} + +template +StringRef ARMExidxInputSection::getOutputSectionName() const { + if (Config->Relocatable) { + // In a relocatable link we consolidate .text.* sections into a single + // .text section. We consolidate all .ARM.exidx.* sections into a single + // ARMExidxOutputSection with a sh_link to the .text OutputSection + // For all other Section Names we preserve the .ARM.exidx. + // name. + InputSectionBase *LT = getLinkedToSection(); + StringRef LinkToName = LT->getSectionName(); + if (!LinkToName.startswith(".text")) + return this->getSectionName(); + } + return ".ARM.exidx"; +} + +template void ARMExidxInputSection::writeTo(uint8_t *Buf) { + // Copy section contents from source object file to output file. + ArrayRef Data = this->getSectionData(); + memcpy(Buf + OutSecOff, Data.data(), Data.size()); + + // Iterate over all relocation sections that apply to this section. + uint8_t *BufEnd = Buf + OutSecOff + Data.size(); + this->relocate(Buf, BufEnd); +} + +template CommonInputSection::CommonInputSection(std::vector Syms) : InputSection(nullptr, &Hdr) { Hdr.sh_size = 0; @@ -752,6 +811,11 @@ template class elf::MipsAbiFlagsInputSection; template class elf::MipsAbiFlagsInputSection; +template class elf::ARMExidxInputSection; +template class elf::ARMExidxInputSection; +template class elf::ARMExidxInputSection; +template class elf::ARMExidxInputSection; + template class elf::CommonInputSection; template class elf::CommonInputSection; template class elf::CommonInputSection; Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -33,6 +33,7 @@ template class InputSectionBase; template class MergeInputSection; template class MipsReginfoInputSection; +template class ARMExidxInputSection; template class OutputSection; template class ObjectFile; template class SharedFile; @@ -63,6 +64,7 @@ MipsReginfo, MipsOptions, MipsAbiFlags, + ARMExidx, Plt, Regular, Reloc, @@ -695,6 +697,29 @@ std::vector Fdes; }; +// A consolidated .ARM.exidx OutputSection containing only +// ARMExidxInputSections. Each ARMExidxInputSection has the SHF_LINK_ORDER +// flag set and a sh_link set to the section index of the executable +// InputSection it describes. The intention being to form a binary searchable +// table that maps the address an exception is thrown from to the exception +// table that handles it. +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf +template +class ARMExidxOutputSection final : public OutputSectionBase { + typedef typename ELFT::uint uintX_t; + typedef OutputSectionBase Base; + +public: + ARMExidxOutputSection(StringRef Name); + void addSection(InputSectionBase *C) override; + void assignOffsets() override; + void writeTo(uint8_t *Buf) override; + void finalize() override; + typename Base::Kind getKind() const override { return Base::ARMExidx; } + static bool classof(const Base *B) { return B->getKind() == Base::ARMExidx; } + std::vector *> Sections; +}; + template class BuildIdSection : public OutputSectionBase { typedef OutputSectionBase Base; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -1801,6 +1801,69 @@ } template +ARMExidxOutputSection::ARMExidxOutputSection(StringRef Name) + : OutputSectionBase(Name, SHT_ARM_EXIDX, SHF_ALLOC | SHF_LINK_ORDER) { +} + +template +void ARMExidxOutputSection::addSection(InputSectionBase *C) { + assert(C->Live); + auto *S = cast>(C); + Sections.push_back(S); + S->OutSec = this; + this->updateAlignment(S->Alignment); +} + +// This function is called after we sort input sections +// and scan relocations to setup sections' offsets. +template void ARMExidxOutputSection::assignOffsets() { + uintX_t Off = 0; + for (ARMExidxInputSection *S : Sections) { + Off = alignTo(Off, S->Alignment); + S->OutSecOff = Off; + Off += S->getSize(); + } + this->Header.sh_size = Off; +} + +template void ARMExidxOutputSection::writeTo(uint8_t *Buf) { + // Order .ARM.Exidx Input Sections in order of the virtual address of + // the sections that the .ARM.exidx sections have a SHF_LINK_ORDER + // dependency on. + auto LinkOrderComp = [](const ARMExidxInputSection *A, + const ARMExidxInputSection *B) { + const InputSection *LinkedToA = A->getLinkedToSection(); + const InputSection *LinkedToB = B->getLinkedToSection(); + if (LinkedToA->OutSec == LinkedToB->OutSec) + return (LinkedToA->OutSecOff < LinkedToB->OutSecOff); + return LinkedToA->OutSec->getVA() < LinkedToB->OutSec->getVA(); + }; + std::stable_sort(Sections.begin(), Sections.end(), LinkOrderComp); + // reassign .ARM.exidx Input Section offsets + // .ARM.exidx sections are always 4-byte aligned and their size + // is always a multiple of 8 so reordering the sections won't change + // the OutputSection size. + assignOffsets(); + + for (ARMExidxInputSection *C : Sections) + C->writeTo(Buf); +} + +template void ARMExidxOutputSection::finalize() { + for (ARMExidxInputSection *S : Sections) { + InputSection *D = S->getLinkedToSection(); + if (D) { + // For Relocatable links all InputSections will have the same + // OutSec. For non-relocatable links the sh_link is irrelevant but + // some ELF processing tools can complain if it is not set so we use + // the first InputSection. + this->Header.sh_link = D->OutSec->SectionIndex; + return; + } + } +} + +template std::pair *, bool> OutputSectionFactory::create(InputSectionBase *C, StringRef OutsecName) { @@ -1828,6 +1891,9 @@ case InputSectionBase::MipsAbiFlags: Sec = new MipsAbiFlagsOutputSection(); break; + case InputSectionBase::ARMExidx: + Sec = new ARMExidxOutputSection(Key.Name); + break; } Out::Pool.emplace_back(Sec); return {Sec, true}; @@ -1960,6 +2026,11 @@ template class MipsAbiFlagsOutputSection; template class MipsAbiFlagsOutputSection; +template class ARMExidxOutputSection; +template class ARMExidxOutputSection; +template class ARMExidxOutputSection; +template class ARMExidxOutputSection; + template class MergeOutputSection; template class MergeOutputSection; template class MergeOutputSection; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -89,10 +89,12 @@ template StringRef elf::getOutputSectionName(InputSectionBase *S) { + if (auto *ARMExidx = dyn_cast>(S)) + return ARMExidx->getOutputSectionName(); StringRef Name = S->getSectionName(); for (StringRef V : {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", - ".tbss.", ".gcc_except_table.", ".tdata.", ".ARM.exidx."}) + ".tbss.", ".gcc_except_table.", ".tdata."}) if (Name.startswith(V)) return V.drop_back(); return Name; @@ -671,6 +673,11 @@ if (auto *S = dyn_cast>(C)) if (S->RelocSection) Fn(*S, *S->RelocSection); + if (auto *S = dyn_cast>(C)) { + for (const Elf_Shdr *RelSec : S->RelocSections) + Fn(*S, *RelSec); + continue; + } } } } @@ -971,6 +978,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; @@ -1001,6 +1009,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. @@ -1024,6 +1034,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) { 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-order.s =================================================================== --- /dev/null +++ test/ELF/arm-exidx-order.s @@ -0,0 +1,236 @@ +// 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 : { *(.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 +// Check that relocatable link maintains SHF_LINK_ORDER and when relinked +// produces same output as a full link +// RUN: ld.lld -r %t %tcantunwind -o %t4 2>&1 +// RUN: llvm-readobj -s %t4 | FileCheck -check-prefix=CHECK-RELOCATABLE %s +// RUN: ld.lld %t4 -o %t5 +// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t5 | FileCheck %s +// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t5 | FileCheck -check-prefix=CHECK-EXIDX %s +// RUN: llvm-readobj --program-headers --sections %t2 | FileCheck -check-prefix=CHECK-PT %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: 114: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: func5: +// CHECK-SCRIPT-NEXT: 118: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: _start: +// CHECK-SCRIPT-NEXT: 11c: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: f1: +// CHECK-SCRIPT-NEXT: 120: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: f2: +// CHECK-SCRIPT-NEXT: 124: 1e ff 2f e1 bx lr +// CHECK-SCRIPT: f3: +// CHECK-SCRIPT-NEXT: 128: 1e ff 2f e1 bx lr +// CHECK-SCRIPT-NEXT: Disassembly of section .func1: +// CHECK-SCRIPT-NEXT: func1: +// CHECK-SCRIPT-NEXT: 174: 1e ff 2f e1 bx lr +// CHECK-SCRIPT-NEXT: Disassembly of section .func2: +// CHECK-SCRIPT-NEXT: func2: +// CHECK-SCRIPT-NEXT: 178: 1e ff 2f e1 bx lr +// CHECK-SCRIPT-NEXT: Disassembly of section .func3: +// CHECK-SCRIPT-NEXT: func3: +// CHECK-SCRIPT-NEXT: 17c: 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 +// will be e and not f. +// 12c - 18 = 114 +// 134 - 1c = 118 +// CHECK-SCRIPT-EXIDX: 012c e8ffff7f 01000000 e4ffff7f 01000000 +// 13c - 20 = 11c +// 144 - 24 = 120 +// CHECK-SCRIPT-EXIDX-NEXT: 013c e0ffff7f 01000000 dcffff7f 01000000 +// 14c - 28 = 124 +// 154 - 2c = 128 +// CHECK-SCRIPT-EXIDX-NEXT: 014c d8ffff7f 01000000 d4ffff7f 01000000 +// 15c + 18 = 174 +// 164 + 14 = 178 +// CHECK-SCRIPT-EXIDX-NEXT: 015c 18000000 01000000 14000000 01000000 +// 16c + 10 = 17c +// CHECK-SCRIPT-EXIDX-NEXT: 016c 10000000 01000000 + +// For a relocatable link check that the .ARM.exidx sections preserve their +// SHF_LINK_ORDER dependencies and the sh_link points at the appropriate +// progbits output section. When the relocatable object is linked as an input +// to a full link the output should match the test above. + +// CHECK-RELOCATABLE: Name: .ARM.exidx +// CHECK-RELOCATABLE-NEXT: Type: SHT_ARM_EXIDX +// CHECK-RELOCATABLE-NEXT: Flags [ +// CHECK-RELOCATABLE-NEXT: SHF_ALLOC +// CHECK-RELOCATABLE-NEXT: SHF_LINK_ORDER +// CHECK-RELOCATABLE: Link: 5 +// CHECK-RELOCATABLE: Name: .ARM.exidx.func1 +// CHECK-RELOCATABLE-NEXT: Type: SHT_ARM_EXIDX +// CHECK-RELOCATABLE-NEXT: Flags [ +// CHECK-RELOCATABLE-NEXT: SHF_ALLOC +// CHECK-RELOCATABLE-NEXT: SHF_LINK_ORDER +// CHECK-RELOCATABLE: Link: 6 +// CHECK-RELOCATABLE: Name: .ARM.exidx.func2 +// CHECK-RELOCATABLE-NEXT: Type: SHT_ARM_EXIDX +// CHECK-RELOCATABLE-NEXT: Flags [ +// CHECK-RELOCATABLE-NEXT: SHF_ALLOC +// CHECK-RELOCATABLE-NEXT: SHF_LINK_ORDER +// CHECK-RELOCATABLE: Link: 7 +// CHECK-RELOCATABLE: Name: .ARM.exidx.func3 +// CHECK-RELOCATABLE-NEXT: Type: SHT_ARM_EXIDX +// CHECK-RELOCATABLE-NEXT: Flags [ +// CHECK-RELOCATABLE-NEXT: SHF_ALLOC +// CHECK-RELOCATABLE-NEXT: SHF_LINK_ORDER +// CHECK-RELOCATABLE: Link: 8 +// CHECK-RELOCATABLE: Section { +// CHECK-RELOCATABLE-NEXT: Index: 5 +// CHECK-RELOCATABLE-NEXT: Name: .text +// CHECK-RELOCATABLE-NEXT: Type: SHT_PROGBITS +// CHECK-RELOCATABLE: Section { +// CHECK-RELOCATABLE-NEXT: Index: 6 +// CHECK-RELOCATABLE-NEXT: Name: .func1 +// CHECK-RELOCATABLE-NEXT: Type: SHT_PROGBITS +// CHECK-RELOCATABLE: Section { +// CHECK-RELOCATABLE-NEXT: Index: 7 +// CHECK-RELOCATABLE-NEXT: Name: .func2 +// CHECK-RELOCATABLE-NEXT: Type: SHT_PROGBITS +// CHECK-RELOCATABLE: Section { +// CHECK-RELOCATABLE-NEXT: Index: 8 +// CHECK-RELOCATABLE-NEXT: Name: .func3 +// CHECK-RELOCATABLE-NEXT: Type: SHT_PROGBITS