According to https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst#the-binary-searched-index-table, to enable binary search over the section, the table must be sorted. ELD generates an unsorted table, reproducible example below. ### Environment - ELD: eld 23.0 — commit 9e74efb3b - Clang / llvm-readelf: 19.1.7. - Host: Linux x86_64. ### Impact The ARM EHABI unwinder — unw_step, _Unwind_Backtrace, _Unwind_ForcedUnwind in libunwind, and equivalent code in libgcc — does a binary search over .ARM.exidx to map a PC to its unwind opcodes. §5.2 of IHI0038 makes that search contract explicit: entries must appear in strictly ascending order of function-start address. Any inversion breaks unwinding — the search can land on the wrong entry, apply the wrong opcodes, and typically stops the walk after one frame or resolves into a neighbouring function. We hit this via failing libunwind tests (forceunwind.pass.cpp, libunwind_01.pass.cpp, libunwind_02.pass.cpp) on five ARM multilib variants in cpullvm. ### Reproducer ``` ; repro.s .syntax unified .thumb .section .text.start, "ax", %progbits .globl _start .thumb_func _start: .fnstart bx lr .cantunwind .fnend .section .text.cluster, "ax", %progbits .thumb_func .globl c00 c00: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c01 c01: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c02 c02: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c03 c03: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c04 c04: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c05 c05: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c06 c06: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c07 c07: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c08 c08: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c09 c09: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c10 c10: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c11 c11: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c12 c12: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c13 c13: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c14 c14: .fnstart bx lr .cantunwind .fnend .thumb_func .globl c15 c15: .fnstart bx lr .cantunwind .fnend ``` ``` ; link.t SECTIONS { .text (0x11000) : { *(.text.cluster) *(.text.start) } .ARM.exidx : { *(.ARM.exidx*) } } ``` ``` $ clang -target armv7em-none-eabi -mfloat-abi=soft -mthumb -c repro.s -o repro.o $ ld.eld -march arm repro.o -T link.t -o repro.out $ llvm-readelf --unwind repro.out ... Entry { FunctionAddress: 0x1101A Model: CantUnwind } Entry { FunctionAddress: 0x1101C Model: CantUnwind } Entry { FunctionAddress: 0x11020 Model: CantUnwind } Entry { FunctionAddress: 0x1101E Model: CantUnwind } ] } } ``` ### Expected vs. observed The 17 functions in the reproducer end up at addresses 0x11000, 0x11002, 0x11004, …, 0x1101E, 0x11020 (16 cluster entries followed by _start, since the script lists *(.text.cluster) *(.text.start)). The .ARM.exidx entries should therefore appear in that same ascending order. Expected (last 5 entries): ``` FunctionAddress: 0x11018 FunctionAddress: 0x1101A FunctionAddress: 0x1101C FunctionAddress: 0x1101E FunctionAddress: 0x11020 ``` Observed (last 5 entries, ELD 23.0): ``` FunctionAddress: 0x11018 FunctionAddress: 0x1101A FunctionAddress: 0x1101C FunctionAddress: 0x11020 ← _start, out of order FunctionAddress: 0x1101E ← c15, should have come before _start ```