Does it make sense for a function to have a personality/LSDA if it has no other unwind info? (Mach-O)

I’m asking because MC doesn’t emit any unwind info if there are no other CFI directives in the function… but only when targeting x86_64 for Mach-O. When targeting arm64, it does actually emit a compact unwind entry pointing to the relevant personality/LSDA. Which behavior is the right one?

Specifically, this is a function that MC will emit unwind info for only when targeting arm64:

_foo:
.cfi_startproc
.cfi_personality 155, _bar
.cfi_lsda 16, Lbar
  ret
.cfi_endproc

When unwinding, if no personality function exists the runtime skips processing the frame. When a personality function does exist, it’s called to do whatever action is needed. On Itanium C++ ABI with __gxx_personality_v0 this means LSDA tables must be present indicating what address range is legal to unwind through because if the table is blank __gxx_personality_v0 will terminate.

In context to your question: I believe both behave identically when unwinding if the one with the personality function has no-op actions. Obviously, the arm64 case requires more binary size to do it so omitting it is the better implementation.

I think the MC layer ought to be conservative – it should do exactly what the assembly tells it to do. Personality functions can have side effects or complicated interactions with the unwinding machinery that MC should not try to understand. It is entirely reasonable for LLVM, on the other hand, to emit different .cfi directives based on knowledge of specific personality functions. It sounds like that’s not how things currently work for MachO x86_64, and that sounds suspicious to me, but I’m pretty unfamiliar with MachO.

True, from the example I assumed the scenario was a no-op personality action getting added but that’s not confirmed.

+1. @int3 some more context would be helpful here:

  1. In your example, what exact personality/LSDA is getting emitted for arm64 but not x86?
  2. Does this behavior occur when there are real unwinding actions or only on “no-op” actions?

It is slightly complicated for darwin platform, because on arm architectures, unwind info is default to compact unwind unless it can’t be represented, but x86 (maybe there is some flag to control), it will use the old dwarf unwind.

The example will produce compact unwind for arm64 but eh_frame section for x86_64. The size of the object file for arm64 is actually significantly smaller because of compact unwind.