[libunwind] Add an interface for dynamic .eh_frame registration

The libgcc runtime library provides __register_frame and
__deregister_frame functions, which can be used by dynamic code
generators to register an .eh_frame section, which contains one or
more Call Frame Information records, each consisting of a Common
Information Entry record followed by one or more Frame Description
Entry records. This libunwind library also provides __register_frame
and __deregister_frame functions, but they are aliases for
__unw_add_dynamic_fde and __unw_remove_dynamic_fde and thus can only
take a single FDE.

This patch adds __unw_add_dynamic_eh_frame_section and
__unw_remove_dynamic_eh_frame_section functions which explicitly use
the .eh_frame format. Clients such as the ORCv2 platform and runtime
can check for these functions and use them if unwinding is being
provided by libunwind, or fall back to __register_frame and
__deregister_frame if unwinding is provided by libgcc.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D111863

NOKEYCHECK=True
GitOrigin-RevId: bab39816085d715e52c2131fa249ccd10178764b
diff --git a/src/libunwind.cpp b/src/libunwind.cpp
index 2bf31b1..48750ce 100644
--- a/src/libunwind.cpp
+++ b/src/libunwind.cpp
@@ -292,6 +292,35 @@
   // fde is own mh_group
   DwarfFDECache<LocalAddressSpace>::removeAllIn((LocalAddressSpace::pint_t)fde);
 }
+
+void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start) {
+  // The eh_frame section start serves as the mh_group
+  unw_word_t mh_group = eh_frame_start;
+  CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
+  CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
+  auto p = (LocalAddressSpace::pint_t)eh_frame_start;
+  while (true) {
+    if (CFI_Parser<LocalAddressSpace>::decodeFDE(
+            LocalAddressSpace::sThisAddressSpace, p, &fdeInfo, &cieInfo,
+            true) == NULL) {
+      DwarfFDECache<LocalAddressSpace>::add((LocalAddressSpace::pint_t)mh_group,
+                                            fdeInfo.pcStart, fdeInfo.pcEnd,
+                                            fdeInfo.fdeStart);
+      p += fdeInfo.fdeLength;
+    } else if (CFI_Parser<LocalAddressSpace>::parseCIE(
+                   LocalAddressSpace::sThisAddressSpace, p, &cieInfo) == NULL) {
+      p += cieInfo.cieLength;
+    } else
+      return;
+  }
+}
+
+void __unw_remove_dynamic_eh_frame_section(unw_word_t eh_frame_start) {
+  // The eh_frame section start serves as the mh_group
+  DwarfFDECache<LocalAddressSpace>::removeAllIn(
+      (LocalAddressSpace::pint_t)eh_frame_start);
+}
+
 #endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
 #endif // !defined(__USING_SJLJ_EXCEPTIONS__)