lhames added a comment.

I've chatted about this with Joerg on a side thread and wanted to summarize my 
understanding of the situation.

The problem is that we have incompatible behaviors for `__register_frame` and 
`__deregister_frame` between unwinders:

- libgcc_s and the FreeBSD port of libunwind accept either null-terminated 
whole frames or single FDEs. If called with single FDEs, FreeBSD's port of 
libunwind will have quadratic cost for registration, as it always walks the 
remainder of the section. I suspect libgcc_s's behavior is similar..
- llvm-project's libunwind accepts only single FDEs.

Attempts to reliably detect the unwinder being used (so that we can call it the 
"best" way) have been flaky, leading to hard errors for clients. We're looking 
for a consistent behavior that we could rely on (at least when we can detect 
that it is available). Ideally, the proposed scheme should allow for efficient 
implementations.

The options that I have considered and discussed with others are:

1. Always registering single frames.

The problem with this is that it forces up front costs on the caller: all FDE 
starts must be identified, and we have one libcall per FDE to register (and 
probably a heap allocation embedded in that call). On top of this, two major 
unwinders (libgcc_s and the FreeBSD port of libunwind) seem like they have poor 
implementations of single-FDE processing that may have quadratic overhead in 
the worst case.

2. Teach libunwind's `_register_frame`  to also accept whole frames.

We could update libunwind's `__register_frame` implementation to also be able 
to take a whole section. For this to be useful though, we would still need some 
reliable way to detect whether this behavior is available on the libunwind 
version being used. A new special symbol would do 
(__unw_register_frame_accepts_whole_sections?), but this feels awkward compared 
to @housel's proposal for a new function with clearly defined behavior.

3. Register .eh_frame_hdr sections.

The .eh_frame_hdr section is a pre-built index of the .eh_frame section. Its 
format is documented in 
https://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html. This 
section is not present in relocatable objects (which LLVM's JIT APIs use as 
input), but is synthesized by the static linker.

For ahead of time compilation this link-time synthesis provides a lot of value 
-- indexing the FDEs once at link times means that you never have to do it at 
launch time.

For a JIT I don't think this provides any value: Indexing of the .eh_frame will 
necessarily be happening at "launch" time. At the same time, forcing synthesis 
of an .eh_frame_hdr section for registration constrains unwinder 
implementations: they definitely can't index lazily (the caller already eagerly 
indexed for them), and if they don't want to use .eh_frame_hdr as their 
internal index data structure then the scheme has wasted two encoded pointers 
per FDE registered, plus 4 bytes and two more encoded values for the 
.eh_frame_hdr header.

4. @housel's solution -- Add a new registration function that is defined to 
take an __eh_frame start address.

This requires no up-front computation, we just pass in the start of the 
.eh_frame section. It places no constraints on the unwinder: Implementations 
are free to index eagerly or lazily as they like, and to choose whatever 
internal representation best suits them.

I think that @housel's solution is the best option, and that we should proceed 
with it.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D111863/new/

https://reviews.llvm.org/D111863

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to