This is an archive of the discontinued LLVM Phabricator instance.

[LLD][ARM] Add terminating sentinel .ARM.exidx table entry
ClosedPublic

Authored by peter.smith on Nov 22 2016, 8:49 AM.

Details

Summary

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.

Diff Detail

Repository
rL LLVM

Event Timeline

peter.smith retitled this revision from to [LLD][ARM] Add terminating sentinel .ARM.exidx table entry.
peter.smith updated this object.
peter.smith added a subscriber: llvm-commits.
ruiu edited edge metadata.Nov 22 2016, 9:49 AM

Now that I'm not sure if that's the best way of doing it because as you mentioned that wouldn't work with linker scripts and might be too hacky. What if you implement it as a SyntheticSection? If you create a SyntehticSection for .ARM.exidx and add that to the end of Symtab<ELFT>::X->Sections, it should naturally be located to end of .ARM.exidx. Fortunately this is not a small feature, so it shouldn't be hard to write two implementations to compare.

ELF/Writer.cpp
1509 ↗(On Diff #78879)

You could assume fresh file is filled with zeros.

1540 ↗(On Diff #78879)

nit: omit {} like five lines above.

peter.smith edited edge metadata.

Thanks for the comments. I'll investigate the alternative implementation tomorrow.

For what it is worth I've added linker script support by checking for .ARM.exidx in flush(). It isn't much code but it isn't particularly clean either.

I've reimplemented using a SyntheticSection for .ARM.exidx. The main difference is that we need to make sure that the synthetic section (which has no link order dependency is sorted at the end of the .ARM.exidx section.

The tests remain the same from the previous version.

ruiu accepted this revision.Nov 23 2016, 9:37 AM
ruiu edited edge metadata.

It's definitely better than the previous one.

ELF/OutputSections.cpp
91 ↗(On Diff #79082)

Maybe it should be more descriptive. We have a sentinel for .ARM.exidx as a synthetic section and want to put it at end of the section.

But do we need this? I think we are using stable_sort, so as long as you add a new section at end of Symtab->Sections, it will go at end of sections.

ELF/SyntheticSections.cpp
1674 ↗(On Diff #79082)

As you just return 8 from getSize, I'd replace sizeof() with 8 as well.

1682 ↗(On Diff #79082)

ri -> RI

ELF/Writer.cpp
1026 ↗(On Diff #79082)

You could use auto.

if (auto *Sec = dyn_cast_or_null<OutputSection<ELFT>>(findSection(".ARM.exidx")))
  if (!Sec->Sections.empty() ...
This revision is now accepted and ready to land.Nov 23 2016, 9:37 AM
This revision was automatically updated to reflect the committed changes.

Thanks for the comments. I've made most of the suggested changes (r287869).

I've simplified the test for sorting in OutputSections.cpp to take advantage of stable_sort but I still need to return before the getLinkOrderDep() function is called as the synthetic has no dependency. I've also kept the alignment value in the synthetic constructor as sizeof(typename ELFT::uint), hard-coding it to 8 isn't quite right as the expected alignment for the .ARM.exidx is 4.