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