Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -108,7 +108,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 @@ -121,6 +121,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/MarkLive.cpp =================================================================== --- ELF/MarkLive.cpp +++ ELF/MarkLive.cpp @@ -253,6 +253,28 @@ // Mark all reachable sections. while (!Q.empty()) forEachSuccessor(*Q.pop_back_val(), Enqueue); + + // Mark all .ARM.exidx sections that reference live sections + if (Config->EMachine == EM_ARM) { + bool ScanRequired = true; + while (ScanRequired) { + for (ObjectFile *F : Symtab::X->getObjectFiles()) { + for (InputSectionBase *Sec : F->getSections()) { + if (Sec && Sec != &InputSection::Discarded && + Sec->Name.startswith(".ARM.exidx")) { + auto *D = Sec->getLinkOrderDep(); + if (D && D->Live) + Enqueue({Sec, 0}); + } + } + } + // Case .ARM.exidx -> .ARM.extab -> personality routine <- .ARM.exidx + ScanRequired = !Q.empty(); + // Mark all reachable sections from live .ARM.exidx + while (!Q.empty()) + forEachSuccessor(*Q.pop_back_val(), Enqueue); + } + } } template void elf::markLive(); 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; @@ -1060,6 +1062,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; @@ -1090,6 +1093,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. @@ -1113,6 +1118,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) { @@ -1397,6 +1406,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(); @@ -1413,6 +1477,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,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,125 @@ +// 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 [ (0x2) +// CHECK-PT-NEXT: SHF_ALLOC (0x2) +// CHECK-PT-NEXT: ] +// CHECK-PT-NEXT: Address: 0x100D4 +// CHECK-PT-NEXT: Offset: 0xD4 +// CHECK-PT-NEXT: Size: 40 + +// 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-gc.s =================================================================== --- /dev/null +++ test/ELF/arm-exidx-gc.s @@ -0,0 +1,153 @@ +// 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 + .align 2 + .global func1 + .type func1, %function +func1: + .fnstart + bx lr + .save {r7, lr} + .setfp r7, sp, #0 + .fnend + + .section .text.unusedfunc1, "ax",%progbits + .align 2 + .global unusedfunc1 + .type unusedfunc1, %function +unusedfunc1: + .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 + + // An unused function with a reference to a .ARM.extab section. Both should + // be removed by gc. + .section .text.unusedfunc2, "ax",%progbits + .align 2 + .global unusedfunc2 + .type unusedfunc2, %function +unusedfunc2: + .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.unusedfunc2 + .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 + +// 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 +// 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-NOT: unusedfunc1 +// CHECK-NOT: unusedfunc2 + +// GC should have removed table entries for unusedfunc1 and unusedfunc2 +// 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 (100e8 + 14 = 100fc = extab entry for func2) +// 100ec + f28 = 11014 = __gxx_personality_v0 +// CHECK-EXIDX-NEXT: 100e4 2c0f0000 14000000 280f0000 01000000 +// 100f4 + f24 = 11018 = __aeabi_unwind_cpp_pr0 +// CHECK-EXIDX-NEXT: 100f4 240f0000 01000000 + + +// CHECK-EXIDX: Contents of section .ARM.extab.text.func2: +// 100fc + f18 = 11018 = __aeabi_unwind_cpp_pr0 (cantunwind) +// CHECK-EXIDX-NEXT: 100fc 180f0000 08849700 ff901601 32000100 +// CHECK-EXIDX-NOT: Contents of section .ARM.extab.text.unusedfunc2: Index: test/ELF/arm-exidx-order.s =================================================================== --- /dev/null +++ test/ELF/arm-exidx-order.s @@ -0,0 +1,176 @@ +// 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 [ (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 @@ -41,9 +41,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