The .ARM.exidx table has an entry for each function with the first entry giving the start address of the function, the table is sorted in ascending order of function address. Given a PC value, the unwinder will search the table for the entry that contains the PC value.
If the table entry happens to be the last, the range of the addresses that the final unwinding table describes will extend to the end of the address space. To prevent an incorrect address outside the address range of the program matching the last entry we follow ld.bfd's example and add a sentinel EXIDX_CANTUNWIND entry at the end of the table. This gives the final real table entry an upper bound.
In addition the llvm libunwind unwinder currently depends on the presence of a sentinel entry https://llvm.org/bugs/show_bug.cgi?id=31091.
This implementation uses the strategy of increasing the size of the .ARM.exidx OutputSection and directly writing in the sentinel that we need.
Regrettably this is defeated by linker scripting as the sizes of the OutputSection are calculated when the script is resolved. I've not yet worked out the best way of fixing this. I'm investigating:
- Finding the .ARM.exidx Output Section command and inserting a . = . +8 at the end.
- When resolving the script addresses, intercept the .ARM.exidx Output and add 8 to the location counter. In effect a hard-coded . = . +8
I'm open to suggestions on how best to resolve this with linker scripts. If it is going to be too messy it may be better to try an alternative implementation that adds a linker generated .ARM.exidx section so that the terminating entry drops out.
In the existing test with a linker script I've used .ARM.exidx : { *(.ARM.exidx*) . = . +8 } so that I can update the tests to what they need to be. In arm-data-prel I've renamed the output section name as in that test case we are just testing the relocation and want to suppress any special handling of .ARM.exidx.