The FDE lookup in unwind-dw2-fde-dip.c uses a binary search on the
.eh_frame_hdr table, but only when the table encoding is
DW_EH_PE_datarel | DW_EH_PE_sdata4 (8-byte entries).  When the
-large-eh-encoding flag produces DW_EH_PE_datarel | DW_EH_PE_sdata8
(16-byte entries), the code falls through to linear_search_fdes(),
regressing FDE lookup from O(log N) to O(N).

Add support for sdata8 table entries in the binary search path by:
- Extending the table_enc check to accept DW_EH_PE_sdata8
- Using a packed union with mode(SI)/mode(DI) fields to read entries
  of either width via TABLE_LOC/TABLE_FDE accessor macros
- Adjusting the alignment check to use 8-byte alignment for sdata8

libgcc/ChangeLog:

        * unwind-dw2-fde-dip.c (find_fde_tail): Support
        DW_EH_PE_datarel | DW_EH_PE_sdata8 table encoding in
        addition to DW_EH_PE_sdata4 for binary search of
        .eh_frame_hdr.

Signed-off-by: Farid Zakaria <[email protected]>
---
 libgcc/unwind-dw2-fde-dip.c | 64 ++++++++++++++++++++++++++++---------
 1 file changed, 49 insertions(+), 15 deletions(-)

diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index 409fbbf7ef3..488f9b5dd45 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -414,11 +414,20 @@ find_fde_tail (_Unwind_Ptr pc,
 
   /* We require here specific table encoding to speed things up.
      Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
-     as base, not the processor specific DW_EH_PE_datarel.  */
+     as base, not the processor specific DW_EH_PE_datarel.
+     Both sdata4 (4-byte) and sdata8 (8-byte) table entries are
+     supported; sdata8 is used by -large-eh-encoding for large
+     binaries where offsets exceed 2GiB.  */
   if (hdr->fde_count_enc != DW_EH_PE_omit
-      && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
+      && (hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4)
+         || hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata8)))
     {
       _Unwind_Ptr fde_count;
+      const int is_sdata8
+       = (hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata8));
+      const size_t entry_size = is_sdata8 ? 16 : 8;
+      const size_t field_size = is_sdata8 ? 8 : 4;
+      const unsigned int align_mask = is_sdata8 ? 7 : 3;
 
       if (__builtin_expect (hdr->fde_count_enc == DW_EH_PE_udata4, 1))
        {
@@ -437,23 +446,45 @@ find_fde_tail (_Unwind_Ptr pc,
       /* Shouldn't happen.  */
       if (fde_count == 0)
        return NULL;
-      if ((((_Unwind_Ptr) p) & 3) == 0)
+      if ((((_Unwind_Ptr) p) & align_mask) == 0)
        {
-         struct fde_table {
-           signed initial_loc __attribute__ ((mode (SI)));
-           signed fde __attribute__ ((mode (SI)));
-         };
-         const struct fde_table *table = (const struct fde_table *) p;
+         const unsigned char *table = p;
          size_t lo, hi, mid;
          _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
          fde *f;
          unsigned int f_enc, f_enc_size;
          _Unwind_Ptr range;
 
+         /* Read a signed field from the table.  For sdata4 entries
+            these are mode (SI) (32-bit); for sdata8, mode (DI) (64-bit).
+            Use the packed unaligned union from unwind-pe.h types.  */
+         union table_entry {
+           signed s4 __attribute__ ((mode (SI)));
+           signed s8 __attribute__ ((mode (DI)));
+         } __attribute__ ((__packed__));
+
+         /* Read the initial_loc field at a given table index.  */
+#define TABLE_LOC(i)                                           \
+         (is_sdata8                                            \
+          ? (_Unwind_Ptr) ((const union table_entry *)         \
+                           (table + (i) * entry_size))->s8     \
+          : (_Unwind_Ptr) ((const union table_entry *)         \
+                           (table + (i) * entry_size))->s4)
+
+         /* Read the fde field at a given table index.  */
+#define TABLE_FDE(i)                                           \
+         (is_sdata8                                            \
+          ? (_Unwind_Ptr) ((const union table_entry *)         \
+                           (table + (i) * entry_size           \
+                            + field_size))->s8                 \
+          : (_Unwind_Ptr) ((const union table_entry *)         \
+                           (table + (i) * entry_size           \
+                            + field_size))->s4)
+
          mid = fde_count - 1;
-         if (pc < table[0].initial_loc + data_base)
+         if (pc < TABLE_LOC (0) + data_base)
            return NULL;
-         else if (pc < table[mid].initial_loc + data_base)
+         else if (pc < TABLE_LOC (mid) + data_base)
            {
              lo = 0;
              hi = mid;
@@ -461,9 +492,9 @@ find_fde_tail (_Unwind_Ptr pc,
              while (lo < hi)
                {
                  mid = (lo + hi) / 2;
-                 if (pc < table[mid].initial_loc + data_base)
+                 if (pc < TABLE_LOC (mid) + data_base)
                    hi = mid;
-                 else if (pc >= table[mid + 1].initial_loc + data_base)
+                 else if (pc >= TABLE_LOC (mid + 1) + data_base)
                    lo = mid + 1;
                  else
                    break;
@@ -472,7 +503,7 @@ find_fde_tail (_Unwind_Ptr pc,
              gcc_assert (lo < hi);
            }
 
-         f = (fde *) (table[mid].fde + data_base);
+         f = (fde *) (TABLE_FDE (mid) + data_base);
          f_enc = get_fde_encoding (f);
          f_enc_size = size_of_encoded_value (f_enc);
 
@@ -494,8 +525,8 @@ find_fde_tail (_Unwind_Ptr pc,
 #endif
            read_encoded_value_with_base (f_enc & 0x0f, 0,
                                          &f->pc_begin[f_enc_size], &range);
-         _Unwind_Ptr func = table[mid].initial_loc + data_base;
-         if (pc < table[mid].initial_loc + data_base + range)
+         _Unwind_Ptr func = TABLE_LOC (mid) + data_base;
+         if (pc < TABLE_LOC (mid) + data_base + range)
            {
              bases->tbase = NULL;
              bases->dbase = (void *) dbase;
@@ -504,6 +535,9 @@ find_fde_tail (_Unwind_Ptr pc,
            }
          else
            return NULL;
+
+#undef TABLE_LOC
+#undef TABLE_FDE
        }
     }
 
-- 
2.52.0

Reply via email to