Debrand for AlmaLinux

This commit is contained in:
Eduard Abdullin 2025-09-15 11:59:05 +00:00 committed by root
commit a9efcc8941
34 changed files with 11547 additions and 6 deletions

View File

@ -0,0 +1,63 @@
commit 90986c5f0aa61cd22a9132486304ba5d12aae6c4
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Nov 22 13:30:23 2021 +0100
libgcc: Remove tbase member from struct unw_eh_callback_data
It is always a null pointer.
libgcc/ChangeLog
* unwind-dw2-fde-dip.c (struct unw_eh_callback_data): Remove
tbase member.
(base_from_cb_data): Adjust.
(_Unwind_IteratePhdrCallback): Likewise.
(_Unwind_Find_FDE): Likewise.
diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index 5095b6830bf79e2e..4a4d990f455e5c11 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -104,7 +104,6 @@ static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases
struct unw_eh_callback_data
{
_Unwind_Ptr pc;
- void *tbase;
void *dbase;
void *func;
const fde *ret;
@@ -154,7 +153,7 @@ base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
return 0;
case DW_EH_PE_textrel:
- return (_Unwind_Ptr) data->tbase;
+ return 0;
case DW_EH_PE_datarel:
return (_Unwind_Ptr) data->dbase;
default:
@@ -431,7 +430,7 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
As soon as GLIBC will provide API so to notify that a library has been
removed, we could cache this (and thus use search_object). */
ob.pc_begin = NULL;
- ob.tbase = data->tbase;
+ ob.tbase = NULL;
ob.dbase = data->dbase;
ob.u.single = (fde *) eh_frame;
ob.s.i = 0;
@@ -461,7 +460,6 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
return ret;
data.pc = (_Unwind_Ptr) pc;
- data.tbase = NULL;
data.dbase = NULL;
data.func = NULL;
data.ret = NULL;
@@ -472,7 +470,7 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
if (data.ret)
{
- bases->tbase = data.tbase;
+ bases->tbase = NULL;
bases->dbase = data.dbase;
bases->func = data.func;
}

View File

@ -0,0 +1,51 @@
commit 94ccaf62c378c3737f7e4b6a80e1160157119171
Author: Thomas Neumann <tneumann@users.sourceforge.net>
Date: Mon Sep 19 18:10:02 2022 +0200
Avoid depending on destructor order
In some scenarios (e.g., when mixing gcc and clang code), it can
happen that frames are deregistered after the lookup structure
has already been destroyed. That in itself would be fine, but
it triggers an assert in __deregister_frame_info_bases that
expects to find the frame.
To avoid that, we now remember that the btree as already been
destroyed and disable the assert in that case.
libgcc/ChangeLog:
* unwind-dw2-fde.c: (release_register_frames) Remember
when the btree has been destroyed.
(__deregister_frame_info_bases) Disable the assert when
shutting down.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index f38efd3c09efc3e9..b0d07ccd53b30f4c 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -48,6 +48,7 @@ typedef __UINTPTR_TYPE__ uintptr_type;
#include "unwind-dw2-btree.h"
static struct btree registered_frames;
+static bool in_shutdown;
static void
release_registered_frames (void) __attribute__ ((destructor (110)));
@@ -57,6 +58,7 @@ release_registered_frames (void)
/* Release the b-tree and all frames. Frame releases that happen later are
* silently ignored */
btree_destroy (&registered_frames);
+ in_shutdown = true;
}
static void
@@ -282,7 +284,7 @@ __deregister_frame_info_bases (const void *begin)
__gthread_mutex_unlock (&object_mutex);
#endif
- gcc_assert (ob);
+ gcc_assert (in_shutdown || ob);
return (void *) ob;
}

View File

@ -0,0 +1,42 @@
commit 386ebf75f4c0342b1f823f4e4aba07abda3288d1
Author: Thomas Neumann <tneumann@users.sourceforge.net>
Date: Fri Sep 23 15:57:13 2022 +0200
fix assert in __deregister_frame_info_bases
When using the atomic fast path deregistering can fail during
program shutdown if the lookup structures are already destroyed.
The assert in __deregister_frame_info_bases takes that into
account. In the non-fast-path case however is not aware of
program shutdown, which caused a compiler error on such platforms.
We fix that by introducing a constant for in_shutdown in
non-fast-path builds.
We also drop the destructor priority, as it is not supported on
all platforms and we no longer rely upon the priority anyway.
libgcc/ChangeLog:
* unwind-dw2-fde.c: Introduce a constant for in_shutdown
for the non-fast-path case. Drop destructor priority.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index b0d07ccd53b30f4c..27fea89dc314ccd0 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -51,7 +51,7 @@ static struct btree registered_frames;
static bool in_shutdown;
static void
-release_registered_frames (void) __attribute__ ((destructor (110)));
+release_registered_frames (void) __attribute__ ((destructor));
static void
release_registered_frames (void)
{
@@ -67,6 +67,8 @@ static void
init_object (struct object *ob);
#else
+/* Without fast path frame deregistration must always succeed. */
+static const int in_shutdown = 0;
/* The unseen_objects list contains objects that have been registered
but not yet categorized in any way. The seen_objects list has had

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
commit acdb24166d13d87c374e578d2ad5d58249171930
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Oct 17 11:09:17 2022 +0200
libgcc: Move cfa_how into potential padding in struct frame_state_reg_info
On many architectures, there is a padding gap after the how array
member, and cfa_how can be moved there. This reduces the size of the
struct and the amount of memory that uw_frame_state_for has to clear.
There is no measurable performance benefit from this on x86-64 (even
though the memset goes from 120 to 112 bytes), but it seems to be a
good idea to do anyway.
libgcc/
* unwind-dw2.h (struct frame_state_reg_info): Move cfa_how member
and reduce its size.
diff --git a/libgcc/unwind-dw2.h b/libgcc/unwind-dw2.h
index 22241b1f0d14cffc..437c785efa4f297d 100644
--- a/libgcc/unwind-dw2.h
+++ b/libgcc/unwind-dw2.h
@@ -50,6 +50,12 @@ typedef struct
} reg[__LIBGCC_DWARF_FRAME_REGISTERS__+1];
unsigned char how[__LIBGCC_DWARF_FRAME_REGISTERS__+1];
+ enum {
+ CFA_UNSET,
+ CFA_REG_OFFSET,
+ CFA_EXP
+ } cfa_how : 8;
+
/* Used to implement DW_CFA_remember_state. */
struct frame_state_reg_info *prev;
@@ -58,11 +64,6 @@ typedef struct
_Unwind_Sword cfa_offset;
_Unwind_Word cfa_reg;
const unsigned char *cfa_exp;
- enum {
- CFA_UNSET,
- CFA_REG_OFFSET,
- CFA_EXP
- } cfa_how;
} regs;
/* The PC described by the current frame state. */

View File

@ -0,0 +1,99 @@
commit e724b0480bfa5ec04f39be8c7290330b495c59de
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Nov 4 10:18:03 2022 +0100
libgcc: Special-case BFD ld unwind table encodings in find_fde_tail
BFD ld (and the other linkers) only produce one encoding of these
values. It is not necessary to use the general
read_encoded_value_with_base decoding routine. This avoids the
data-dependent branches in its implementation.
libgcc/
* unwind-dw2-fde-dip.c (find_fde_tail): Special-case encoding
values actually used by BFD ld.
diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index 25f2e44c5823cf64..d4821d7d19950f15 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -396,10 +396,21 @@ find_fde_tail (_Unwind_Ptr pc,
if (hdr->version != 1)
return NULL;
- p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
- base_from_cb_data (hdr->eh_frame_ptr_enc,
- dbase),
- p, &eh_frame);
+ if (__builtin_expect (hdr->eh_frame_ptr_enc == (DW_EH_PE_sdata4
+ | DW_EH_PE_pcrel), 1))
+ {
+ /* Specialized version of read_encoded_value_with_base, based on what
+ BFD ld generates. */
+ signed value __attribute__ ((mode (SI)));
+ memcpy (&value, p, sizeof (value));
+ p += sizeof (value);
+ dbase = value; /* No adjustment because pcrel has base 0. */
+ }
+ else
+ p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
+ base_from_cb_data (hdr->eh_frame_ptr_enc,
+ dbase),
+ p, &eh_frame);
/* We require here specific table encoding to speed things up.
Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
@@ -409,10 +420,20 @@ find_fde_tail (_Unwind_Ptr pc,
{
_Unwind_Ptr fde_count;
- p = read_encoded_value_with_base (hdr->fde_count_enc,
- base_from_cb_data (hdr->fde_count_enc,
- dbase),
- p, &fde_count);
+ if (__builtin_expect (hdr->fde_count_enc == DW_EH_PE_udata4, 1))
+ {
+ /* Specialized version of read_encoded_value_with_base, based on
+ what BFD ld generates. */
+ unsigned value __attribute__ ((mode (SI)));
+ memcpy (&value, p, sizeof (value));
+ p += sizeof (value);
+ fde_count = value;
+ }
+ else
+ p = read_encoded_value_with_base (hdr->fde_count_enc,
+ base_from_cb_data (hdr->fde_count_enc,
+ dbase),
+ p, &fde_count);
/* Shouldn't happen. */
if (fde_count == 0)
return NULL;
@@ -454,8 +475,25 @@ find_fde_tail (_Unwind_Ptr pc,
f = (fde *) (table[mid].fde + data_base);
f_enc = get_fde_encoding (f);
f_enc_size = size_of_encoded_value (f_enc);
- read_encoded_value_with_base (f_enc & 0x0f, 0,
- &f->pc_begin[f_enc_size], &range);
+
+ /* BFD ld uses DW_EH_PE_sdata4 | DW_EH_PE_pcrel on non-FDPIC targets,
+ so optimize for that.
+
+ This optimization is not valid for FDPIC targets. f_enc & 0x0f as
+ passed to read_encoded_value_with_base masks away the base flags,
+ but they are implicit for FDPIC. */
+#ifndef __FDPIC__
+ if (__builtin_expect (f_enc == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel),
+ 1))
+ {
+ signed value __attribute__ ((mode (SI)));
+ memcpy (&value, &f->pc_begin[f_enc_size], sizeof (value));
+ range = value;
+ }
+ else
+#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)
{

View File

@ -0,0 +1,337 @@
commit 1c118c9970600117700cc12284587e0238de6bbe
Author: Thomas Neumann <tneumann@users.sourceforge.net>
Date: Tue Nov 22 08:41:54 2022 +0100
speed up end_fde_sort using radix sort
When registering a dynamic unwinding frame the fde list is sorted.
Previously, we split the list into a sorted and an unsorted part,
sorted the later using heap sort, and merged both. That can be
quite slow due to the large number of (expensive) comparisons.
This patch replaces that logic with a radix sort instead. The
radix sort uses the same amount of memory as the old logic,
using the second list as auxiliary space, and it includes two
techniques to speed up sorting: First, it computes the pointer
addresses for blocks of values, reducing the decoding overhead.
And it recognizes when the data has reached a sorted state,
allowing for early termination. When running out of memory
we fall back to pure heap sort, as before.
For this test program
\#include <cstdio>
int main(int argc, char** argv) {
return 0;
}
compiled with g++ -O -o hello -static hello.c we get with
perf stat -r 200 on a 5950X the following performance numbers:
old logic:
0,20 msec task-clock
930.834 cycles
3.079.765 instructions
0,00030478 +- 0,00000237 seconds time elapsed
new logic:
0,10 msec task-clock
473.269 cycles
1.239.077 instructions
0,00021119 +- 0,00000168 seconds time elapsed
libgcc/ChangeLog:
* unwind-dw2-fde.c: Use radix sort instead of split+sort+merge.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index 27fea89dc314ccd0..a0d9bfb9f7d34ec1 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -456,22 +456,52 @@ fde_mixed_encoding_compare (struct object *ob, const fde *x, const fde *y)
typedef int (*fde_compare_t) (struct object *, const fde *, const fde *);
+// The extractor functions compute the pointer values for a block of
+// fdes. The block processing hides the call overhead.
-/* This is a special mix of insertion sort and heap sort, optimized for
- the data sets that actually occur. They look like
- 101 102 103 127 128 105 108 110 190 111 115 119 125 160 126 129 130.
- I.e. a linearly increasing sequence (coming from functions in the text
- section), with additionally a few unordered elements (coming from functions
- in gnu_linkonce sections) whose values are higher than the values in the
- surrounding linear sequence (but not necessarily higher than the values
- at the end of the linear sequence!).
- The worst-case total run time is O(N) + O(n log (n)), where N is the
- total number of FDEs and n is the number of erratic ones. */
+static void
+fde_unencoded_extract (struct object *ob __attribute__ ((unused)),
+ _Unwind_Ptr *target, const fde **x, int count)
+{
+ for (int index = 0; index < count; ++index)
+ memcpy (target + index, x[index]->pc_begin, sizeof (_Unwind_Ptr));
+}
+
+static void
+fde_single_encoding_extract (struct object *ob, _Unwind_Ptr *target,
+ const fde **x, int count)
+{
+ _Unwind_Ptr base;
+
+ base = base_from_object (ob->s.b.encoding, ob);
+ for (int index = 0; index < count; ++index)
+ read_encoded_value_with_base (ob->s.b.encoding, base, x[index]->pc_begin,
+ target + index);
+}
+
+static void
+fde_mixed_encoding_extract (struct object *ob, _Unwind_Ptr *target,
+ const fde **x, int count)
+{
+ for (int index = 0; index < count; ++index)
+ {
+ int encoding = get_fde_encoding (x[index]);
+ read_encoded_value_with_base (encoding, base_from_object (encoding, ob),
+ x[index]->pc_begin, target + index);
+ }
+}
+
+typedef void (*fde_extractor_t) (struct object *, _Unwind_Ptr *, const fde **,
+ int);
+
+// Data is is sorted using radix sort if possible, using an temporary
+// auxiliary data structure of the same size as the input. When running
+// out of memory do in-place heap sort.
struct fde_accumulator
{
struct fde_vector *linear;
- struct fde_vector *erratic;
+ struct fde_vector *aux;
};
static inline int
@@ -485,8 +515,8 @@ start_fde_sort (struct fde_accumulator *accu, size_t count)
if ((accu->linear = malloc (size)))
{
accu->linear->count = 0;
- if ((accu->erratic = malloc (size)))
- accu->erratic->count = 0;
+ if ((accu->aux = malloc (size)))
+ accu->aux->count = 0;
return 1;
}
else
@@ -500,59 +530,6 @@ fde_insert (struct fde_accumulator *accu, const fde *this_fde)
accu->linear->array[accu->linear->count++] = this_fde;
}
-/* Split LINEAR into a linear sequence with low values and an erratic
- sequence with high values, put the linear one (of longest possible
- length) into LINEAR and the erratic one into ERRATIC. This is O(N).
-
- Because the longest linear sequence we are trying to locate within the
- incoming LINEAR array can be interspersed with (high valued) erratic
- entries. We construct a chain indicating the sequenced entries.
- To avoid having to allocate this chain, we overlay it onto the space of
- the ERRATIC array during construction. A final pass iterates over the
- chain to determine what should be placed in the ERRATIC array, and
- what is the linear sequence. This overlay is safe from aliasing. */
-
-static inline void
-fde_split (struct object *ob, fde_compare_t fde_compare,
- struct fde_vector *linear, struct fde_vector *erratic)
-{
- static const fde *marker;
- size_t count = linear->count;
- const fde *const *chain_end = &marker;
- size_t i, j, k;
-
- /* This should optimize out, but it is wise to make sure this assumption
- is correct. Should these have different sizes, we cannot cast between
- them and the overlaying onto ERRATIC will not work. */
- gcc_assert (sizeof (const fde *) == sizeof (const fde **));
-
- for (i = 0; i < count; i++)
- {
- const fde *const *probe;
-
- for (probe = chain_end;
- probe != &marker && fde_compare (ob, linear->array[i], *probe) < 0;
- probe = chain_end)
- {
- chain_end = (const fde *const*) erratic->array[probe - linear->array];
- erratic->array[probe - linear->array] = NULL;
- }
- erratic->array[i] = (const fde *) chain_end;
- chain_end = &linear->array[i];
- }
-
- /* Each entry in LINEAR which is part of the linear sequence we have
- discovered will correspond to a non-NULL entry in the chain we built in
- the ERRATIC array. */
- for (i = j = k = 0; i < count; i++)
- if (erratic->array[i])
- linear->array[j++] = linear->array[i];
- else
- erratic->array[k++] = linear->array[i];
- linear->count = j;
- erratic->count = k;
-}
-
#define SWAP(x,y) do { const fde * tmp = x; x = y; y = tmp; } while (0)
/* Convert a semi-heap to a heap. A semi-heap is a heap except possibly
@@ -615,59 +592,116 @@ frame_heapsort (struct object *ob, fde_compare_t fde_compare,
#undef SWAP
}
-/* Merge V1 and V2, both sorted, and put the result into V1. */
+// Radix sort data in V1 using V2 as aux memory. Runtime O(n).
static inline void
-fde_merge (struct object *ob, fde_compare_t fde_compare,
- struct fde_vector *v1, struct fde_vector *v2)
+fde_radixsort (struct object *ob, fde_extractor_t fde_extractor,
+ struct fde_vector *v1, struct fde_vector *v2)
{
- size_t i1, i2;
- const fde * fde2;
-
- i2 = v2->count;
- if (i2 > 0)
+#define FANOUTBITS 8
+#define FANOUT (1 << FANOUTBITS)
+#define BLOCKSIZE 128
+ const unsigned rounds
+ = (__CHAR_BIT__ * sizeof (_Unwind_Ptr) + FANOUTBITS - 1) / FANOUTBITS;
+ const fde **a1 = v1->array, **a2 = v2->array;
+ _Unwind_Ptr ptrs[BLOCKSIZE + 1];
+ unsigned n = v1->count;
+ for (unsigned round = 0; round != rounds; ++round)
{
- i1 = v1->count;
- do
+ unsigned counts[FANOUT] = {0};
+ unsigned violations = 0;
+
+ // Count the number of elements per bucket and check if we are already
+ // sorted.
+ _Unwind_Ptr last = 0;
+ for (unsigned i = 0; i < n;)
+ {
+ unsigned chunk = ((n - i) <= BLOCKSIZE) ? (n - i) : BLOCKSIZE;
+ fde_extractor (ob, ptrs + 1, a1 + i, chunk);
+ ptrs[0] = last;
+ for (unsigned j = 0; j < chunk; ++j)
+ {
+ unsigned b = (ptrs[j + 1] >> (round * FANOUTBITS)) & (FANOUT - 1);
+ counts[b]++;
+ // Use summation instead of an if to eliminate branches.
+ violations += ptrs[j + 1] < ptrs[j];
+ }
+ i += chunk;
+ last = ptrs[chunk];
+ }
+
+ // Stop if we are already sorted.
+ if (!violations)
+ {
+ // The sorted data is in a1 now.
+ a2 = a1;
+ break;
+ }
+
+ // Compute the prefix sum.
+ unsigned sum = 0;
+ for (unsigned i = 0; i != FANOUT; ++i)
+ {
+ unsigned s = sum;
+ sum += counts[i];
+ counts[i] = s;
+ }
+
+ // Place all elements.
+ for (unsigned i = 0; i < n;)
{
- i2--;
- fde2 = v2->array[i2];
- while (i1 > 0 && fde_compare (ob, v1->array[i1-1], fde2) > 0)
+ unsigned chunk = ((n - i) <= BLOCKSIZE) ? (n - i) : BLOCKSIZE;
+ fde_extractor (ob, ptrs, a1 + i, chunk);
+ for (unsigned j = 0; j < chunk; ++j)
{
- v1->array[i1+i2] = v1->array[i1-1];
- i1--;
+ unsigned b = (ptrs[j] >> (round * FANOUTBITS)) & (FANOUT - 1);
+ a2[counts[b]++] = a1[i + j];
}
- v1->array[i1+i2] = fde2;
+ i += chunk;
}
- while (i2 > 0);
- v1->count += v2->count;
+
+ // Swap a1 and a2.
+ const fde **tmp = a1;
+ a1 = a2;
+ a2 = tmp;
}
+#undef BLOCKSIZE
+#undef FANOUT
+#undef FANOUTBITS
+
+ // The data is in a2 now, move in place if needed.
+ if (a2 != v1->array)
+ memcpy (v1->array, a2, sizeof (const fde *) * n);
}
static inline void
end_fde_sort (struct object *ob, struct fde_accumulator *accu, size_t count)
{
- fde_compare_t fde_compare;
-
gcc_assert (!accu->linear || accu->linear->count == count);
- if (ob->s.b.mixed_encoding)
- fde_compare = fde_mixed_encoding_compare;
- else if (ob->s.b.encoding == DW_EH_PE_absptr)
- fde_compare = fde_unencoded_compare;
- else
- fde_compare = fde_single_encoding_compare;
-
- if (accu->erratic)
+ if (accu->aux)
{
- fde_split (ob, fde_compare, accu->linear, accu->erratic);
- gcc_assert (accu->linear->count + accu->erratic->count == count);
- frame_heapsort (ob, fde_compare, accu->erratic);
- fde_merge (ob, fde_compare, accu->linear, accu->erratic);
- free (accu->erratic);
+ fde_extractor_t fde_extractor;
+ if (ob->s.b.mixed_encoding)
+ fde_extractor = fde_mixed_encoding_extract;
+ else if (ob->s.b.encoding == DW_EH_PE_absptr)
+ fde_extractor = fde_unencoded_extract;
+ else
+ fde_extractor = fde_single_encoding_extract;
+
+ fde_radixsort (ob, fde_extractor, accu->linear, accu->aux);
+ free (accu->aux);
}
else
{
- /* We've not managed to malloc an erratic array,
+ fde_compare_t fde_compare;
+ if (ob->s.b.mixed_encoding)
+ fde_compare = fde_mixed_encoding_compare;
+ else if (ob->s.b.encoding == DW_EH_PE_absptr)
+ fde_compare = fde_unencoded_compare;
+ else
+ fde_compare = fde_single_encoding_compare;
+
+ /* We've not managed to malloc an aux array,
so heap sort in the linear one. */
frame_heapsort (ob, fde_compare, accu->linear);
}

View File

@ -0,0 +1,143 @@
commit 6e56633daae79f514b0e71f4d9849bcd8d9ce71f
Author: Thomas Neumann <tneumann@users.sourceforge.net>
Date: Fri Dec 9 18:23:44 2022 +0100
initialize fde objects lazily
When registering an unwind frame with __register_frame_info_bases
we currently initialize that fde object eagerly. This has the
advantage that it is immutable afterwards and we can safely
access it from multiple threads, but it has the disadvantage
that we pay the initialization cost even if the application
never throws an exception.
This commit changes the logic to initialize the objects lazily.
The objects themselves are inserted into the b-tree when
registering the frame, but the sorted fde_vector is
not constructed yet. Only on the first time that an
exception tries to pass through the registered code the
object is initialized. We notice that with a double checking,
first doing a relaxed load of the sorted bit and then re-checking
under a mutex when the object was not initialized yet.
Note that the check must implicitly be safe concering a concurrent
frame deregistration, as trying the deregister a frame that is
on the unwinding path of a concurrent exception is inherently racy.
libgcc/ChangeLog:
* unwind-dw2-fde.c: Initialize fde object lazily when
the first exception tries to pass through.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index a0d9bfb9f7d34ec1..efcf9490469ad1a0 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -63,8 +63,6 @@ release_registered_frames (void)
static void
get_pc_range (const struct object *ob, uintptr_type *range);
-static void
-init_object (struct object *ob);
#else
/* Without fast path frame deregistration must always succeed. */
@@ -76,6 +74,7 @@ static const int in_shutdown = 0;
by decreasing value of pc_begin. */
static struct object *unseen_objects;
static struct object *seen_objects;
+#endif
#ifdef __GTHREAD_MUTEX_INIT
static __gthread_mutex_t object_mutex = __GTHREAD_MUTEX_INIT;
@@ -103,7 +102,6 @@ init_object_mutex_once (void)
static __gthread_mutex_t object_mutex;
#endif
#endif
-#endif
/* Called from crtbegin.o to register the unwind info for an object. */
@@ -126,10 +124,7 @@ __register_frame_info_bases (const void *begin, struct object *ob,
#endif
#ifdef ATOMIC_FDE_FAST_PATH
- // Initialize eagerly to avoid locking later
- init_object (ob);
-
- // And register the frame
+ // Register the frame in the b-tree
uintptr_type range[2];
get_pc_range (ob, range);
btree_insert (&registered_frames, range[0], range[1] - range[0], ob);
@@ -180,10 +175,7 @@ __register_frame_info_table_bases (void *begin, struct object *ob,
ob->s.b.encoding = DW_EH_PE_omit;
#ifdef ATOMIC_FDE_FAST_PATH
- // Initialize eagerly to avoid locking later
- init_object (ob);
-
- // And register the frame
+ // Register the frame in the b-tree
uintptr_type range[2];
get_pc_range (ob, range);
btree_insert (&registered_frames, range[0], range[1] - range[0], ob);
@@ -926,7 +918,15 @@ init_object (struct object* ob)
accu.linear->orig_data = ob->u.single;
ob->u.sort = accu.linear;
+#ifdef ATOMIC_FDE_FAST_PATH
+ // We must update the sorted bit with an atomic operation
+ struct object tmp;
+ tmp.s.b = ob->s.b;
+ tmp.s.b.sorted = 1;
+ __atomic_store (&(ob->s.b), &(tmp.s.b), __ATOMIC_RELEASE);
+#else
ob->s.b.sorted = 1;
+#endif
}
#ifdef ATOMIC_FDE_FAST_PATH
@@ -1164,6 +1164,21 @@ search_object (struct object* ob, void *pc)
}
}
+#ifdef ATOMIC_FDE_FAST_PATH
+
+// Check if the object was already initialized
+static inline bool
+is_object_initialized (struct object *ob)
+{
+ // We have to use acquire atomics for the read, which
+ // is a bit involved as we read from a bitfield
+ struct object tmp;
+ __atomic_load (&(ob->s.b), &(tmp.s.b), __ATOMIC_ACQUIRE);
+ return tmp.s.b.sorted;
+}
+
+#endif
+
const fde *
_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
{
@@ -1175,6 +1190,21 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
if (!ob)
return NULL;
+ // Initialize the object lazily
+ if (!is_object_initialized (ob))
+ {
+ // Check again under mutex
+ init_object_mutex_once ();
+ __gthread_mutex_lock (&object_mutex);
+
+ if (!ob->s.b.sorted)
+ {
+ init_object (ob);
+ }
+
+ __gthread_mutex_unlock (&object_mutex);
+ }
+
f = search_object (ob, pc);
#else

View File

@ -0,0 +1,703 @@
commit 8fdef16cd5d1b89359db3cd9a9768ab2d1b5081f
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Jan 3 16:47:32 2023 +0100
libgcc: Specialize execute_cfa_program in DWARF unwinder for alignments [redo]
The parameters fs->data_align and fs->code_align always have fixed
values for a particular target in GCC-generated code. Specialize
execute_cfa_program for these values, to avoid multiplications.
gcc/c-family/
* c-cppbuiltin.cc (c_cpp_builtins): Define
__LIBGCC_DWARF_CIE_DATA_ALIGNMENT__.
libgcc/
* unwind-dw2-execute_cfa.h: New file. Extracted from
the execute_cfa_program function in unwind-dw2.c.
* unwind-dw2.c (execute_cfa_program_generic): New function.
(execute_cfa_program_specialized): Likewise.
(execute_cfa_program): Call execute_cfa_program_specialized
or execute_cfa_program_generic, as appropriate.
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index 11e015bdb87b0f9b..a5369eb51b07f0ab 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -1408,6 +1408,9 @@ c_cpp_builtins (cpp_reader *pfile)
#endif
builtin_define_with_int_value ("__LIBGCC_DWARF_FRAME_REGISTERS__",
DWARF_FRAME_REGISTERS);
+ builtin_define_with_int_value ("__LIBGCC_DWARF_CIE_DATA_ALIGNMENT__",
+ DWARF_CIE_DATA_ALIGNMENT);
+
#ifdef EH_RETURN_STACKADJ_RTX
cpp_define (pfile, "__LIBGCC_EH_RETURN_STACKADJ_RTX__");
#endif
diff --git a/libgcc/unwind-dw2-execute_cfa.h b/libgcc/unwind-dw2-execute_cfa.h
new file mode 100644
index 0000000000000000..dd97b7866686a361
--- /dev/null
+++ b/libgcc/unwind-dw2-execute_cfa.h
@@ -0,0 +1,322 @@
+/* DWARF2 exception handling CFA execution engine.
+ Copyright (C) 1997-2022 Free Software Foundation, Inc.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ Under Section 7 of GPL version 3, you are granted additional
+ permissions described in the GCC Runtime Library Exception, version
+ 3.1, as published by the Free Software Foundation.
+
+ You should have received a copy of the GNU General Public License and
+ a copy of the GCC Runtime Library Exception along with this program;
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* This file is included from unwind-dw2.c to specialize the code for certain
+ values of DATA_ALIGN and CODE_ALIGN. These macros must be defined prior to
+ including this file. */
+
+{
+ struct frame_state_reg_info *unused_rs = NULL;
+
+ /* Don't allow remember/restore between CIE and FDE programs. */
+ fs->regs.prev = NULL;
+
+ /* The comparison with the return address uses < rather than <= because
+ we are only interested in the effects of code before the call; for a
+ noreturn function, the return address may point to unrelated code with
+ a different stack configuration that we are not interested in. We
+ assume that the call itself is unwind info-neutral; if not, or if
+ there are delay instructions that adjust the stack, these must be
+ reflected at the point immediately before the call insn.
+ In signal frames, return address is after last completed instruction,
+ so we add 1 to return address to make the comparison <=. */
+ while (insn_ptr < insn_end
+ && fs->pc < context->ra + _Unwind_IsSignalFrame (context))
+ {
+ unsigned char insn = *insn_ptr++;
+ _uleb128_t reg, utmp;
+ _sleb128_t offset, stmp;
+
+ if ((insn & 0xc0) == DW_CFA_advance_loc)
+ fs->pc += (insn & 0x3f) * CODE_ALIGN;
+ else if ((insn & 0xc0) == DW_CFA_offset)
+ {
+ reg = insn & 0x3f;
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ offset = (_Unwind_Sword) utmp * DATA_ALIGN;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.how[reg] = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
+ }
+ else if ((insn & 0xc0) == DW_CFA_restore)
+ {
+ reg = insn & 0x3f;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ fs->regs.how[reg] = REG_UNSAVED;
+ }
+ else switch (insn)
+ {
+ case DW_CFA_set_loc:
+ {
+ _Unwind_Ptr pc;
+
+ insn_ptr = read_encoded_value (context, fs->fde_encoding,
+ insn_ptr, &pc);
+ fs->pc = (void *) pc;
+ }
+ break;
+
+ case DW_CFA_advance_loc1:
+ fs->pc += read_1u (insn_ptr) * CODE_ALIGN;
+ insn_ptr += 1;
+ break;
+ case DW_CFA_advance_loc2:
+ fs->pc += read_2u (insn_ptr) * CODE_ALIGN;
+ insn_ptr += 2;
+ break;
+ case DW_CFA_advance_loc4:
+ fs->pc += read_4u (insn_ptr) * CODE_ALIGN;
+ insn_ptr += 4;
+ break;
+
+ case DW_CFA_offset_extended:
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ offset = (_Unwind_Sword) utmp * DATA_ALIGN;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.how[reg] = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
+ break;
+
+ case DW_CFA_restore_extended:
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ /* FIXME, this is wrong; the CIE might have said that the
+ register was saved somewhere. */
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ fs->regs.how[reg] = REG_UNSAVED;
+ break;
+
+ case DW_CFA_same_value:
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ fs->regs.how[reg] = REG_UNSAVED;
+ break;
+
+ case DW_CFA_undefined:
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ fs->regs.how[reg] = REG_UNDEFINED;
+ break;
+
+ case DW_CFA_nop:
+ break;
+
+ case DW_CFA_register:
+ {
+ _uleb128_t reg2;
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ insn_ptr = read_uleb128 (insn_ptr, &reg2);
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.how[reg] = REG_SAVED_REG;
+ fs->regs.reg[reg].loc.reg = (_Unwind_Word)reg2;
+ }
+ }
+ break;
+
+ case DW_CFA_remember_state:
+ {
+ struct frame_state_reg_info *new_rs;
+ if (unused_rs)
+ {
+ new_rs = unused_rs;
+ unused_rs = unused_rs->prev;
+ }
+ else
+ new_rs = alloca (sizeof (struct frame_state_reg_info));
+
+ *new_rs = fs->regs;
+ fs->regs.prev = new_rs;
+ }
+ break;
+
+ case DW_CFA_restore_state:
+ {
+ struct frame_state_reg_info *old_rs = fs->regs.prev;
+ fs->regs = *old_rs;
+ old_rs->prev = unused_rs;
+ unused_rs = old_rs;
+ }
+ break;
+
+ case DW_CFA_def_cfa:
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ fs->regs.cfa_reg = (_Unwind_Word)utmp;
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ fs->regs.cfa_offset = (_Unwind_Word)utmp;
+ fs->regs.cfa_how = CFA_REG_OFFSET;
+ break;
+
+ case DW_CFA_def_cfa_register:
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ fs->regs.cfa_reg = (_Unwind_Word)utmp;
+ fs->regs.cfa_how = CFA_REG_OFFSET;
+ break;
+
+ case DW_CFA_def_cfa_offset:
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ fs->regs.cfa_offset = utmp;
+ /* cfa_how deliberately not set. */
+ break;
+
+ case DW_CFA_def_cfa_expression:
+ fs->regs.cfa_exp = insn_ptr;
+ fs->regs.cfa_how = CFA_EXP;
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ insn_ptr += utmp;
+ break;
+
+ case DW_CFA_expression:
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.how[reg] = REG_SAVED_EXP;
+ fs->regs.reg[reg].loc.exp = insn_ptr;
+ }
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ insn_ptr += utmp;
+ break;
+
+ /* Dwarf3. */
+ case DW_CFA_offset_extended_sf:
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ insn_ptr = read_sleb128 (insn_ptr, &stmp);
+ offset = stmp * DATA_ALIGN;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.how[reg] = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
+ break;
+
+ case DW_CFA_def_cfa_sf:
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ fs->regs.cfa_reg = (_Unwind_Word)utmp;
+ insn_ptr = read_sleb128 (insn_ptr, &stmp);
+ fs->regs.cfa_offset = (_Unwind_Sword)stmp;
+ fs->regs.cfa_how = CFA_REG_OFFSET;
+ fs->regs.cfa_offset *= DATA_ALIGN;
+ break;
+
+ case DW_CFA_def_cfa_offset_sf:
+ insn_ptr = read_sleb128 (insn_ptr, &stmp);
+ fs->regs.cfa_offset = (_Unwind_Sword)stmp;
+ fs->regs.cfa_offset *= DATA_ALIGN;
+ /* cfa_how deliberately not set. */
+ break;
+
+ case DW_CFA_val_offset:
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ offset = (_Unwind_Sword) utmp * DATA_ALIGN;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
+ break;
+
+ case DW_CFA_val_offset_sf:
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ insn_ptr = read_sleb128 (insn_ptr, &stmp);
+ offset = stmp * DATA_ALIGN;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
+ break;
+
+ case DW_CFA_val_expression:
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.how[reg] = REG_SAVED_VAL_EXP;
+ fs->regs.reg[reg].loc.exp = insn_ptr;
+ }
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ insn_ptr += utmp;
+ break;
+
+ case DW_CFA_GNU_window_save:
+#if defined (__aarch64__) && !defined (__ILP32__)
+ /* This CFA is multiplexed with Sparc. On AArch64 it's used to toggle
+ return address signing status. */
+ reg = DWARF_REGNUM_AARCH64_RA_STATE;
+ gcc_assert (fs->regs.how[reg] == REG_UNSAVED);
+ fs->regs.reg[reg].loc.offset ^= 1;
+#else
+ /* ??? Hardcoded for SPARC register window configuration. */
+ if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)
+ for (reg = 16; reg < 32; ++reg)
+ {
+ fs->regs.how[reg] = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
+ }
+#endif
+ break;
+
+ case DW_CFA_GNU_args_size:
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ context->args_size = (_Unwind_Word)utmp;
+ break;
+
+ case DW_CFA_GNU_negative_offset_extended:
+ /* Obsoleted by DW_CFA_offset_extended_sf, but used by
+ older PowerPC code. */
+ insn_ptr = read_uleb128 (insn_ptr, &reg);
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ offset = (_Unwind_Word) utmp * DATA_ALIGN;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.how[reg] = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = -offset;
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+}
+
+#undef DATA_ALIGN
+#undef CODE_ALIGN
diff --git a/libgcc/unwind-dw2.c b/libgcc/unwind-dw2.c
index daebcb8bf7d215fe..701552634c6e87c5 100644
--- a/libgcc/unwind-dw2.c
+++ b/libgcc/unwind-dw2.c
@@ -947,302 +947,43 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end,
instruction sequence to decode, current register information and
CIE info, and the PC range to evaluate. */
+static void __attribute__ ((__noinline__))
+execute_cfa_program_generic (const unsigned char *insn_ptr,
+ const unsigned char *insn_end,
+ struct _Unwind_Context *context,
+ _Unwind_FrameState *fs)
+{
+#define DATA_ALIGN fs->data_align
+#define CODE_ALIGN fs->code_align
+#include "unwind-dw2-execute_cfa.h"
+}
+
+static inline void
+execute_cfa_program_specialized (const unsigned char *insn_ptr,
+ const unsigned char *insn_end,
+ struct _Unwind_Context *context,
+ _Unwind_FrameState *fs)
+{
+#define DATA_ALIGN __LIBGCC_DWARF_CIE_DATA_ALIGNMENT__
+ /* GCC always uses 1 even on architectures with a fixed instruction
+ width. */
+#define CODE_ALIGN 1
+#include "unwind-dw2-execute_cfa.h"
+}
+
static void
execute_cfa_program (const unsigned char *insn_ptr,
const unsigned char *insn_end,
struct _Unwind_Context *context,
_Unwind_FrameState *fs)
{
- struct frame_state_reg_info *unused_rs = NULL;
-
- /* Don't allow remember/restore between CIE and FDE programs. */
- fs->regs.prev = NULL;
-
- /* The comparison with the return address uses < rather than <= because
- we are only interested in the effects of code before the call; for a
- noreturn function, the return address may point to unrelated code with
- a different stack configuration that we are not interested in. We
- assume that the call itself is unwind info-neutral; if not, or if
- there are delay instructions that adjust the stack, these must be
- reflected at the point immediately before the call insn.
- In signal frames, return address is after last completed instruction,
- so we add 1 to return address to make the comparison <=. */
- while (insn_ptr < insn_end
- && fs->pc < context->ra + _Unwind_IsSignalFrame (context))
- {
- unsigned char insn = *insn_ptr++;
- _uleb128_t reg, utmp;
- _sleb128_t offset, stmp;
-
- if ((insn & 0xc0) == DW_CFA_advance_loc)
- fs->pc += (insn & 0x3f) * fs->code_align;
- else if ((insn & 0xc0) == DW_CFA_offset)
- {
- reg = insn & 0x3f;
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- offset = (_Unwind_Sword) utmp * fs->data_align;
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- {
- fs->regs.how[reg] = REG_SAVED_OFFSET;
- fs->regs.reg[reg].loc.offset = offset;
- }
- }
- else if ((insn & 0xc0) == DW_CFA_restore)
- {
- reg = insn & 0x3f;
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- fs->regs.how[reg] = REG_UNSAVED;
- }
- else switch (insn)
- {
- case DW_CFA_set_loc:
- {
- _Unwind_Ptr pc;
-
- insn_ptr = read_encoded_value (context, fs->fde_encoding,
- insn_ptr, &pc);
- fs->pc = (void *) pc;
- }
- break;
-
- case DW_CFA_advance_loc1:
- fs->pc += read_1u (insn_ptr) * fs->code_align;
- insn_ptr += 1;
- break;
- case DW_CFA_advance_loc2:
- fs->pc += read_2u (insn_ptr) * fs->code_align;
- insn_ptr += 2;
- break;
- case DW_CFA_advance_loc4:
- fs->pc += read_4u (insn_ptr) * fs->code_align;
- insn_ptr += 4;
- break;
-
- case DW_CFA_offset_extended:
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- offset = (_Unwind_Sword) utmp * fs->data_align;
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- {
- fs->regs.how[reg] = REG_SAVED_OFFSET;
- fs->regs.reg[reg].loc.offset = offset;
- }
- break;
-
- case DW_CFA_restore_extended:
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- /* FIXME, this is wrong; the CIE might have said that the
- register was saved somewhere. */
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- fs->regs.how[reg] = REG_UNSAVED;
- break;
-
- case DW_CFA_same_value:
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- fs->regs.how[reg] = REG_UNSAVED;
- break;
-
- case DW_CFA_undefined:
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- fs->regs.how[reg] = REG_UNDEFINED;
- break;
-
- case DW_CFA_nop:
- break;
-
- case DW_CFA_register:
- {
- _uleb128_t reg2;
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- insn_ptr = read_uleb128 (insn_ptr, &reg2);
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- {
- fs->regs.how[reg] = REG_SAVED_REG;
- fs->regs.reg[reg].loc.reg = (_Unwind_Word)reg2;
- }
- }
- break;
-
- case DW_CFA_remember_state:
- {
- struct frame_state_reg_info *new_rs;
- if (unused_rs)
- {
- new_rs = unused_rs;
- unused_rs = unused_rs->prev;
- }
- else
- new_rs = alloca (sizeof (struct frame_state_reg_info));
-
- *new_rs = fs->regs;
- fs->regs.prev = new_rs;
- }
- break;
-
- case DW_CFA_restore_state:
- {
- struct frame_state_reg_info *old_rs = fs->regs.prev;
- fs->regs = *old_rs;
- old_rs->prev = unused_rs;
- unused_rs = old_rs;
- }
- break;
-
- case DW_CFA_def_cfa:
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- fs->regs.cfa_reg = (_Unwind_Word)utmp;
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- fs->regs.cfa_offset = (_Unwind_Word)utmp;
- fs->regs.cfa_how = CFA_REG_OFFSET;
- break;
-
- case DW_CFA_def_cfa_register:
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- fs->regs.cfa_reg = (_Unwind_Word)utmp;
- fs->regs.cfa_how = CFA_REG_OFFSET;
- break;
-
- case DW_CFA_def_cfa_offset:
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- fs->regs.cfa_offset = utmp;
- /* cfa_how deliberately not set. */
- break;
-
- case DW_CFA_def_cfa_expression:
- fs->regs.cfa_exp = insn_ptr;
- fs->regs.cfa_how = CFA_EXP;
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- insn_ptr += utmp;
- break;
-
- case DW_CFA_expression:
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- {
- fs->regs.how[reg] = REG_SAVED_EXP;
- fs->regs.reg[reg].loc.exp = insn_ptr;
- }
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- insn_ptr += utmp;
- break;
-
- /* Dwarf3. */
- case DW_CFA_offset_extended_sf:
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- insn_ptr = read_sleb128 (insn_ptr, &stmp);
- offset = stmp * fs->data_align;
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- {
- fs->regs.how[reg] = REG_SAVED_OFFSET;
- fs->regs.reg[reg].loc.offset = offset;
- }
- break;
-
- case DW_CFA_def_cfa_sf:
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- fs->regs.cfa_reg = (_Unwind_Word)utmp;
- insn_ptr = read_sleb128 (insn_ptr, &stmp);
- fs->regs.cfa_offset = (_Unwind_Sword)stmp;
- fs->regs.cfa_how = CFA_REG_OFFSET;
- fs->regs.cfa_offset *= fs->data_align;
- break;
-
- case DW_CFA_def_cfa_offset_sf:
- insn_ptr = read_sleb128 (insn_ptr, &stmp);
- fs->regs.cfa_offset = (_Unwind_Sword)stmp;
- fs->regs.cfa_offset *= fs->data_align;
- /* cfa_how deliberately not set. */
- break;
-
- case DW_CFA_val_offset:
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- offset = (_Unwind_Sword) utmp * fs->data_align;
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- {
- fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
- fs->regs.reg[reg].loc.offset = offset;
- }
- break;
-
- case DW_CFA_val_offset_sf:
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- insn_ptr = read_sleb128 (insn_ptr, &stmp);
- offset = stmp * fs->data_align;
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- {
- fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
- fs->regs.reg[reg].loc.offset = offset;
- }
- break;
-
- case DW_CFA_val_expression:
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- {
- fs->regs.how[reg] = REG_SAVED_VAL_EXP;
- fs->regs.reg[reg].loc.exp = insn_ptr;
- }
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- insn_ptr += utmp;
- break;
-
- case DW_CFA_GNU_window_save:
-#if defined (__aarch64__) && !defined (__ILP32__)
- /* This CFA is multiplexed with Sparc. On AArch64 it's used to toggle
- return address signing status. */
- reg = DWARF_REGNUM_AARCH64_RA_STATE;
- gcc_assert (fs->regs.how[reg] == REG_UNSAVED);
- fs->regs.reg[reg].loc.offset ^= 1;
-#else
- /* ??? Hardcoded for SPARC register window configuration. */
- if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)
- for (reg = 16; reg < 32; ++reg)
- {
- fs->regs.how[reg] = REG_SAVED_OFFSET;
- fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
- }
-#endif
- break;
-
- case DW_CFA_GNU_args_size:
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- context->args_size = (_Unwind_Word)utmp;
- break;
-
- case DW_CFA_GNU_negative_offset_extended:
- /* Obsoleted by DW_CFA_offset_extended_sf, but used by
- older PowerPC code. */
- insn_ptr = read_uleb128 (insn_ptr, &reg);
- insn_ptr = read_uleb128 (insn_ptr, &utmp);
- offset = (_Unwind_Word) utmp * fs->data_align;
- reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
- if (UNWIND_COLUMN_IN_RANGE (reg))
- {
- fs->regs.how[reg] = REG_SAVED_OFFSET;
- fs->regs.reg[reg].loc.offset = -offset;
- }
- break;
-
- default:
- gcc_unreachable ();
- }
- }
+ if (fs->data_align == __LIBGCC_DWARF_CIE_DATA_ALIGNMENT__
+ && fs->code_align == 1)
+ execute_cfa_program_specialized (insn_ptr, insn_end, context, fs);
+ else
+ execute_cfa_program_generic (insn_ptr, insn_end, context, fs);
}
+
/* Given the _Unwind_Context CONTEXT for a stack frame, look up the FDE for
its caller and decode it into FS. This function also sets the

View File

@ -0,0 +1,146 @@
commit c98cd1df22fbe0829149e346a1ba9bf1f0be8a40
Author: Wilco Dijkstra <wilco.dijkstra@arm.com>
Date: Tue Jan 3 15:57:46 2023 +0000
libgcc: Fix uninitialized RA signing on AArch64 [PR107678]
A recent change only initializes the regs.how[] during Dwarf unwinding
which resulted in an uninitialized offset used in return address signing
and random failures during unwinding. The fix is to encode the return
address signing state in REG_UNSAVED and a new state REG_UNSAVED_ARCHEXT.
libgcc/
PR target/107678
* unwind-dw2.h (REG_UNSAVED_ARCHEXT): Add new enum.
* unwind-dw2.c (uw_update_context_1): Add REG_UNSAVED_ARCHEXT case.
* unwind-dw2-execute_cfa.h: Use REG_UNSAVED_ARCHEXT/REG_UNSAVED to
encode the return address signing state.
* config/aarch64/aarch64-unwind.h (aarch64_demangle_return_addr)
Check current return address signing state.
(aarch64_frob_update_contex): Remove.
diff --git a/libgcc/config/aarch64/aarch64-unwind.h b/libgcc/config/aarch64/aarch64-unwind.h
index 466e4235991485ea..2fddadc57564348f 100644
--- a/libgcc/config/aarch64/aarch64-unwind.h
+++ b/libgcc/config/aarch64/aarch64-unwind.h
@@ -29,8 +29,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#define MD_DEMANGLE_RETURN_ADDR(context, fs, addr) \
aarch64_demangle_return_addr (context, fs, addr)
-#define MD_FROB_UPDATE_CONTEXT(context, fs) \
- aarch64_frob_update_context (context, fs)
static inline int
aarch64_cie_signed_with_b_key (struct _Unwind_Context *context)
@@ -55,42 +53,28 @@ aarch64_cie_signed_with_b_key (struct _Unwind_Context *context)
static inline void *
aarch64_demangle_return_addr (struct _Unwind_Context *context,
- _Unwind_FrameState *fs ATTRIBUTE_UNUSED,
+ _Unwind_FrameState *fs,
_Unwind_Word addr_word)
{
void *addr = (void *)addr_word;
- if (context->flags & RA_SIGNED_BIT)
+ const int reg = DWARF_REGNUM_AARCH64_RA_STATE;
+
+ if (fs->regs.how[reg] == REG_UNSAVED)
+ return addr;
+
+ /* Return-address signing state is toggled by DW_CFA_GNU_window_save (where
+ REG_UNSAVED/REG_UNSAVED_ARCHEXT means RA signing is disabled/enabled),
+ or set by a DW_CFA_expression. */
+ if (fs->regs.how[reg] == REG_UNSAVED_ARCHEXT
+ || (_Unwind_GetGR (context, reg) & 0x1) != 0)
{
_Unwind_Word salt = (_Unwind_Word) context->cfa;
if (aarch64_cie_signed_with_b_key (context) != 0)
return __builtin_aarch64_autib1716 (addr, salt);
return __builtin_aarch64_autia1716 (addr, salt);
}
- else
- return addr;
-}
-
-/* Do AArch64 private initialization on CONTEXT based on frame info FS. Mark
- CONTEXT as return address signed if bit 0 of DWARF_REGNUM_AARCH64_RA_STATE is
- set. */
-
-static inline void
-aarch64_frob_update_context (struct _Unwind_Context *context,
- _Unwind_FrameState *fs)
-{
- const int reg = DWARF_REGNUM_AARCH64_RA_STATE;
- int ra_signed;
- if (fs->regs.how[reg] == REG_UNSAVED)
- ra_signed = fs->regs.reg[reg].loc.offset & 0x1;
- else
- ra_signed = _Unwind_GetGR (context, reg) & 0x1;
- if (ra_signed)
- /* The flag is used for re-authenticating EH handler's address. */
- context->flags |= RA_SIGNED_BIT;
- else
- context->flags &= ~RA_SIGNED_BIT;
- return;
+ return addr;
}
#endif /* defined AARCH64_UNWIND_H && defined __ILP32__ */
diff --git a/libgcc/unwind-dw2-execute_cfa.h b/libgcc/unwind-dw2-execute_cfa.h
index dd97b7866686a361..0166f85965d4a5ee 100644
--- a/libgcc/unwind-dw2-execute_cfa.h
+++ b/libgcc/unwind-dw2-execute_cfa.h
@@ -278,10 +278,15 @@
case DW_CFA_GNU_window_save:
#if defined (__aarch64__) && !defined (__ILP32__)
/* This CFA is multiplexed with Sparc. On AArch64 it's used to toggle
- return address signing status. */
+ return address signing status. REG_UNSAVED/REG_UNSAVED_ARCHEXT
+ mean RA signing is disabled/enabled. */
reg = DWARF_REGNUM_AARCH64_RA_STATE;
- gcc_assert (fs->regs.how[reg] == REG_UNSAVED);
- fs->regs.reg[reg].loc.offset ^= 1;
+ gcc_assert (fs->regs.how[reg] == REG_UNSAVED
+ || fs->regs.how[reg] == REG_UNSAVED_ARCHEXT);
+ if (fs->regs.how[reg] == REG_UNSAVED)
+ fs->regs.how[reg] = REG_UNSAVED_ARCHEXT;
+ else
+ fs->regs.how[reg] = REG_UNSAVED;
#else
/* ??? Hardcoded for SPARC register window configuration. */
if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)
diff --git a/libgcc/unwind-dw2.c b/libgcc/unwind-dw2.c
index 701552634c6e87c5..280ec2eb4df3d2a2 100644
--- a/libgcc/unwind-dw2.c
+++ b/libgcc/unwind-dw2.c
@@ -137,9 +137,6 @@ struct _Unwind_Context
#define SIGNAL_FRAME_BIT ((~(_Unwind_Word) 0 >> 1) + 1)
/* Context which has version/args_size/by_value fields. */
#define EXTENDED_CONTEXT_BIT ((~(_Unwind_Word) 0 >> 2) + 1)
- /* Bit reserved on AArch64, return address has been signed with A or B
- key. */
-#define RA_SIGNED_BIT ((~(_Unwind_Word) 0 >> 3) + 1)
_Unwind_Word flags;
/* 0 for now, can be increased when further fields are added to
struct _Unwind_Context. */
@@ -1200,6 +1197,7 @@ uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs)
{
case REG_UNSAVED:
case REG_UNDEFINED:
+ case REG_UNSAVED_ARCHEXT:
break;
case REG_SAVED_OFFSET:
diff --git a/libgcc/unwind-dw2.h b/libgcc/unwind-dw2.h
index 437c785efa4f297d..44f63e2eb31298d8 100644
--- a/libgcc/unwind-dw2.h
+++ b/libgcc/unwind-dw2.h
@@ -29,6 +29,7 @@ enum {
REG_SAVED_EXP,
REG_SAVED_VAL_OFFSET,
REG_SAVED_VAL_EXP,
+ REG_UNSAVED_ARCHEXT, /* Target specific extension. */
REG_UNDEFINED
};

View File

@ -0,0 +1,34 @@
commit 9be9be828dc9020735bc7eacddd1ceae1aeedb1b
Author: Sören Tempel <soeren+git@soeren-tempel.net>
Date: Sun May 14 19:30:21 2023 +0200
fix assert in __deregister_frame_info_bases
The assertion in __deregister_frame_info_bases assumes that for every
frame something was inserted into the lookup data structure by
__register_frame_info_bases. Unfortunately, this does not necessarily
hold true as the btree_insert call in __register_frame_info_bases will
not insert anything for empty ranges. Therefore, we need to explicitly
account for such empty ranges in the assertion as `ob` will be a null
pointer for such ranges, hence causing the assertion to fail.
Signed-off-by: Sören Tempel <soeren@soeren-tempel.net>
libgcc/ChangeLog:
* unwind-dw2-fde.c: Accept empty ranges when deregistering frames.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index efcf9490469ad1a0..fdf52396e8576b79 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -278,7 +278,9 @@ __deregister_frame_info_bases (const void *begin)
__gthread_mutex_unlock (&object_mutex);
#endif
- gcc_assert (in_shutdown || ob);
+ // If we didn't find anything in the lookup data structures then they
+ // were either already destroyed or we tried to remove an empty range.
+ gcc_assert (in_shutdown || ((range[1] - range[0]) == 0 || ob));
return (void *) ob;
}

View File

@ -0,0 +1,147 @@
commit f58bf16f672cda3ac55f92f12e258c817ece6e3c
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Nov 22 13:30:23 2021 +0100
libgcc: Remove dbase member from struct unw_eh_callback_data if NULL
Only bfin, frv, i386 and nios2 need this member at present.
libgcc/ChangeLog
* unwind-dw2-fde-dip.c (NEED_DBASE_MEMBER): Define.
(struct unw_eh_callback_data): Make dbase member conditional.
(unw_eh_callback_data_dbase): New function.
(base_from_cb_data): Simplify for the non-dbase case.
(_Unwind_IteratePhdrCallback): Adjust.
(_Unwind_Find_FDE): Likewise.
diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index 4a4d990f455e5c11..3f302826d2d49074 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -101,15 +101,35 @@ static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases
#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
#endif
+#ifdef CRT_GET_RFIB_DATA
+#define NEED_DBASE_MEMBER 1
+#else
+#define NEED_DBASE_MEMBER 0
+#endif
+
struct unw_eh_callback_data
{
_Unwind_Ptr pc;
+#if NEED_DBASE_MEMBER
void *dbase;
+#endif
void *func;
const fde *ret;
int check_cache;
};
+/* Returns DATA->dbase if available, else NULL. */
+static inline _Unwind_Ptr
+unw_eh_callback_data_dbase (const struct unw_eh_callback_data *data
+ __attribute__ ((unused)))
+{
+#if NEED_DBASE_MEMBER
+ return (_Unwind_Ptr) data->dbase;
+#else
+ return 0;
+#endif
+}
+
struct unw_eh_frame_hdr
{
unsigned char version;
@@ -139,9 +159,11 @@ static struct frame_hdr_cache_element *frame_hdr_cache_head;
/* Like base_of_encoded_value, but take the base from a struct
unw_eh_callback_data instead of an _Unwind_Context. */
-static _Unwind_Ptr
-base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
+static inline _Unwind_Ptr
+base_from_cb_data (unsigned char encoding __attribute__ ((unused)),
+ _Unwind_Ptr dbase __attribute__ ((unused)))
{
+#if NEED_DBASE_MEMBER
if (encoding == DW_EH_PE_omit)
return 0;
@@ -155,10 +177,13 @@ base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
case DW_EH_PE_textrel:
return 0;
case DW_EH_PE_datarel:
- return (_Unwind_Ptr) data->dbase;
+ return dbase;
default:
gcc_unreachable ();
}
+#else /* !NEED_DBASE_MEMBER */
+ return 0;
+#endif
}
static int
@@ -358,9 +383,10 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
# endif
#endif
+ _Unwind_Ptr dbase = unw_eh_callback_data_dbase (data);
p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
base_from_cb_data (hdr->eh_frame_ptr_enc,
- data),
+ dbase),
(const unsigned char *) (hdr + 1),
&eh_frame);
@@ -374,7 +400,7 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
p = read_encoded_value_with_base (hdr->fde_count_enc,
base_from_cb_data (hdr->fde_count_enc,
- data),
+ dbase),
p, &fde_count);
/* Shouldn't happen. */
if (fde_count == 0)
@@ -431,7 +457,7 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
removed, we could cache this (and thus use search_object). */
ob.pc_begin = NULL;
ob.tbase = NULL;
- ob.dbase = data->dbase;
+ ob.dbase = (void *) dbase;
ob.u.single = (fde *) eh_frame;
ob.s.i = 0;
ob.s.b.mixed_encoding = 1; /* Need to assume worst case. */
@@ -442,7 +468,7 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
unsigned int encoding = get_fde_encoding (data->ret);
read_encoded_value_with_base (encoding,
- base_from_cb_data (encoding, data),
+ base_from_cb_data (encoding, dbase),
data->ret->pc_begin, &func);
data->func = (void *) func;
}
@@ -460,7 +486,9 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
return ret;
data.pc = (_Unwind_Ptr) pc;
+#if NEED_DBASE_MEMBER
data.dbase = NULL;
+#endif
data.func = NULL;
data.ret = NULL;
data.check_cache = 1;
@@ -471,7 +499,11 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
if (data.ret)
{
bases->tbase = NULL;
+#if NEED_DBASE_MEMBER
bases->dbase = data.dbase;
+#else
+ bases->dbase = NULL;
+#endif
bases->func = data.func;
}
return data.ret;

View File

@ -0,0 +1,38 @@
commit 30adfb85ff994c0faa0cc556ba46838b218263f5
Author: Thomas Neumann <tneumann@users.sourceforge.net>
Date: Mon May 15 14:59:22 2023 +0200
fix assert in non-atomic path
The non-atomic path does not have range information,
we have to adjust the assert handle that case, too.
libgcc/ChangeLog:
* unwind-dw2-fde.c: Fix assert in non-atomic path.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index fdf52396e8576b79..9b0c229efa5427a9 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -240,6 +240,7 @@ __deregister_frame_info_bases (const void *begin)
// And remove
ob = btree_remove (&registered_frames, range[0]);
+ bool empty_table = (range[1] - range[0]) == 0;
#else
init_object_mutex_once ();
__gthread_mutex_lock (&object_mutex);
@@ -276,11 +277,12 @@ __deregister_frame_info_bases (const void *begin)
out:
__gthread_mutex_unlock (&object_mutex);
+ const int empty_table = 0; // The non-atomic path stores all tables.
#endif
// If we didn't find anything in the lookup data structures then they
// were either already destroyed or we tried to remove an empty range.
- gcc_assert (in_shutdown || ((range[1] - range[0]) == 0 || ob));
+ gcc_assert (in_shutdown || (empty_table || ob));
return (void *) ob;
}

View File

@ -0,0 +1,31 @@
commit 5cf60b6ba111f4169305c7832b063b000e9ec36a
Author: Thomas Neumann <tneumann@users.sourceforge.net>
Date: Tue May 2 16:21:09 2023 +0200
release the sorted FDE array when deregistering a frame [PR109685]
The atomic fastpath bypasses the code that releases the sort
array which was lazily allocated during unwinding. We now
check after deregistering if there is an array to free.
libgcc/ChangeLog:
PR libgcc/109685
* unwind-dw2-fde.c: Free sort array in atomic fast path.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index 9b0c229efa5427a9..0fd2fc54aa651350 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -241,6 +241,12 @@ __deregister_frame_info_bases (const void *begin)
// And remove
ob = btree_remove (&registered_frames, range[0]);
bool empty_table = (range[1] - range[0]) == 0;
+
+ // Deallocate the sort array if any.
+ if (ob && ob->s.b.sorted)
+ {
+ free (ob->u.sort);
+ }
#else
init_object_mutex_once ();
__gthread_mutex_lock (&object_mutex);

View File

@ -0,0 +1,51 @@
commit 38e88d41f50d844f1404172657ef7e8372014ef6
Author: Thomas Neumann <tneumann@users.sourceforge.net>
Date: Wed May 10 12:33:49 2023 +0200
fix radix sort on 32bit platforms [PR109670]
The radix sort uses two buffers, a1 for input and a2 for output.
After every digit the role of the two buffers is swapped.
When terminating the sort early the code made sure the output
was in a2. However, when we run out of bits, as can happen on
32bit platforms, the sorted result was in a1, as we had just
swapped a1 and a2.
This patch fixes the problem by unconditionally having a1 as
output after every loop iteration.
This bug manifested itself only on 32bit platforms and even then
only in some circumstances, as it needs frames where a swap
is required due to differences in the top-most byte, which is
affected by ASLR. The new logic was validated by exhaustive
search over 32bit input values.
libgcc/ChangeLog:
PR libgcc/109670
* unwind-dw2-fde.c: Fix radix sort buffer management.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index 0fd2fc54aa651350..41b8c2e9380bc45b 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -634,8 +634,6 @@ fde_radixsort (struct object *ob, fde_extractor_t fde_extractor,
// Stop if we are already sorted.
if (!violations)
{
- // The sorted data is in a1 now.
- a2 = a1;
break;
}
@@ -670,9 +668,9 @@ fde_radixsort (struct object *ob, fde_extractor_t fde_extractor,
#undef FANOUT
#undef FANOUTBITS
- // The data is in a2 now, move in place if needed.
- if (a2 != v1->array)
- memcpy (v1->array, a2, sizeof (const fde *) * n);
+ // The data is in a1 now, move in place if needed.
+ if (a1 != v1->array)
+ memcpy (v1->array, a1, sizeof (const fde *) * n);
}
static inline void

View File

@ -0,0 +1,33 @@
commit 49310a993308492348119f4033e4db0bda4fe46a
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Jun 6 11:01:07 2023 +0200
libgcc: Fix eh_frame fast path in find_fde_tail
The eh_frame value is only used by linear_search_fdes, not the binary
search directly in find_fde_tail, so the bug is not immediately
apparent with most programs.
Fixes commit e724b0480bfa5ec04f39be8c7290330b495c59de ("libgcc:
Special-case BFD ld unwind table encodings in find_fde_tail").
libgcc/
PR libgcc/109712
* unwind-dw2-fde-dip.c (find_fde_tail): Correct fast path for
parsing eh_frame.
diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index d4821d7d19950f15..b46e95dc8f88ac5c 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -403,8 +403,8 @@ find_fde_tail (_Unwind_Ptr pc,
BFD ld generates. */
signed value __attribute__ ((mode (SI)));
memcpy (&value, p, sizeof (value));
+ eh_frame = p + value;
p += sizeof (value);
- dbase = value; /* No adjustment because pcrel has base 0. */
}
else
p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,

View File

@ -0,0 +1,28 @@
commit 104b09005229ef48a79a33511ea192bb3ec3c415
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Jul 11 06:19:39 2023 +0200
libgcc: Fix -Wint-conversion warning in find_fde_tail
Fixes commit r14-1614-g49310a99330849 ("libgcc: Fix eh_frame fast path
in find_fde_tail").
libgcc/
PR libgcc/110179
* unwind-dw2-fde-dip.c (find_fde_tail): Add cast to avoid
implicit conversion of pointer value to integer.
diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index b46e95dc8f88ac5c..e08154c1442d748f 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -403,7 +403,7 @@ find_fde_tail (_Unwind_Ptr pc,
BFD ld generates. */
signed value __attribute__ ((mode (SI)));
memcpy (&value, p, sizeof (value));
- eh_frame = p + value;
+ eh_frame = (_Unwind_Ptr) (p + value);
p += sizeof (value);
}
else

View File

@ -0,0 +1,97 @@
commit c46bded78f3733ad1312d141ebf1ae541032a48b
Author: Thomas Neumann <thomas.neumann@in.tum.de>
Date: Fri Aug 11 09:20:27 2023 -0600
preserve base pointer for __deregister_frame [PR110956]
Original bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110956
Rainer Orth successfully tested the patch on Solaris with a full bootstrap.
Some uncommon unwinding table encodings need to access the base pointer
for address computations. We do not have that information in calls to
__deregister_frame_info_bases, and previously simply used nullptr as
base pointer. That is usually fine, but for some Solaris i386 shared
libraries that results in wrong address computations.
To fix this problem we now associate the unwinding object with
the table pointer itself, which is always known, in addition to
the PC range. When deregistering a frame, we first locate the object
using the table pointer, and then use the base pointer stored within
the object to compute the PC range.
libgcc/ChangeLog:
PR libgcc/110956
* unwind-dw2-fde.c: Associate object with address of unwinding
table.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index 41b8c2e9380bc45b..5d6fb29acd440563 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -124,6 +124,9 @@ __register_frame_info_bases (const void *begin, struct object *ob,
#endif
#ifdef ATOMIC_FDE_FAST_PATH
+ // Register the object itself to know the base pointer on deregistration.
+ btree_insert (&registered_frames, (uintptr_type) begin, 1, ob);
+
// Register the frame in the b-tree
uintptr_type range[2];
get_pc_range (ob, range);
@@ -175,6 +178,9 @@ __register_frame_info_table_bases (void *begin, struct object *ob,
ob->s.b.encoding = DW_EH_PE_omit;
#ifdef ATOMIC_FDE_FAST_PATH
+ // Register the object itself to know the base pointer on deregistration.
+ btree_insert (&registered_frames, (uintptr_type) begin, 1, ob);
+
// Register the frame in the b-tree
uintptr_type range[2];
get_pc_range (ob, range);
@@ -225,22 +231,17 @@ __deregister_frame_info_bases (const void *begin)
return ob;
#ifdef ATOMIC_FDE_FAST_PATH
- // Find the corresponding PC range
- struct object lookupob;
- lookupob.tbase = 0;
- lookupob.dbase = 0;
- lookupob.u.single = begin;
- lookupob.s.i = 0;
- lookupob.s.b.encoding = DW_EH_PE_omit;
-#ifdef DWARF2_OBJECT_END_PTR_EXTENSION
- lookupob.fde_end = NULL;
-#endif
- uintptr_type range[2];
- get_pc_range (&lookupob, range);
+ // Find the originally registered object to get the base pointer.
+ ob = btree_remove (&registered_frames, (uintptr_type) begin);
- // And remove
- ob = btree_remove (&registered_frames, range[0]);
- bool empty_table = (range[1] - range[0]) == 0;
+ // Remove the corresponding PC range.
+ if (ob)
+ {
+ uintptr_type range[2];
+ get_pc_range (ob, range);
+ if (range[0] != range[1])
+ btree_remove (&registered_frames, range[0]);
+ }
// Deallocate the sort array if any.
if (ob && ob->s.b.sorted)
@@ -283,12 +284,11 @@ __deregister_frame_info_bases (const void *begin)
out:
__gthread_mutex_unlock (&object_mutex);
- const int empty_table = 0; // The non-atomic path stores all tables.
#endif
// If we didn't find anything in the lookup data structures then they
// were either already destroyed or we tried to remove an empty range.
- gcc_assert (in_shutdown || (empty_table || ob));
+ gcc_assert (in_shutdown || ob);
return (void *) ob;
}

View File

@ -0,0 +1,112 @@
commit a364148530c28645ce87adbc58a66c9f32a325ab
Author: Thomas Neumann <tneumann@users.sourceforge.net>
Date: Mon Mar 11 14:35:20 2024 +0100
handle unwind tables that are embedded within unwinding code [PR111731]
Original bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111731
The unwinding mechanism registers both the code range and the unwind
table itself within a b-tree lookup structure. That data structure
assumes that is consists of non-overlappping intervals. This
becomes a problem if the unwinding table is embedded within the
code itself, as now the intervals do overlap.
To fix this problem we now keep the unwind tables in a separate
b-tree, which prevents the overlap.
libgcc/ChangeLog:
PR libgcc/111731
* unwind-dw2-fde.c: Split unwind ranges if they contain the
unwind table.
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index 5d6fb29acd440563..421068b538abc66d 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -48,6 +48,7 @@ typedef __UINTPTR_TYPE__ uintptr_type;
#include "unwind-dw2-btree.h"
static struct btree registered_frames;
+static struct btree registered_objects;
static bool in_shutdown;
static void
@@ -58,6 +59,7 @@ release_registered_frames (void)
/* Release the b-tree and all frames. Frame releases that happen later are
* silently ignored */
btree_destroy (&registered_frames);
+ btree_destroy (&registered_objects);
in_shutdown = true;
}
@@ -103,6 +105,21 @@ static __gthread_mutex_t object_mutex;
#endif
#endif
+#ifdef ATOMIC_FDE_FAST_PATH
+// Register the pc range for a given object in the lookup structure.
+static void
+register_pc_range_for_object (uintptr_type begin, struct object *ob)
+{
+ // Register the object itself to know the base pointer on deregistration.
+ btree_insert (&registered_objects, begin, 1, ob);
+
+ // Register the frame in the b-tree
+ uintptr_type range[2];
+ get_pc_range (ob, range);
+ btree_insert (&registered_frames, range[0], range[1] - range[0], ob);
+}
+#endif
+
/* Called from crtbegin.o to register the unwind info for an object. */
void
@@ -124,13 +141,7 @@ __register_frame_info_bases (const void *begin, struct object *ob,
#endif
#ifdef ATOMIC_FDE_FAST_PATH
- // Register the object itself to know the base pointer on deregistration.
- btree_insert (&registered_frames, (uintptr_type) begin, 1, ob);
-
- // Register the frame in the b-tree
- uintptr_type range[2];
- get_pc_range (ob, range);
- btree_insert (&registered_frames, range[0], range[1] - range[0], ob);
+ register_pc_range_for_object ((uintptr_type) begin, ob);
#else
init_object_mutex_once ();
__gthread_mutex_lock (&object_mutex);
@@ -178,13 +189,7 @@ __register_frame_info_table_bases (void *begin, struct object *ob,
ob->s.b.encoding = DW_EH_PE_omit;
#ifdef ATOMIC_FDE_FAST_PATH
- // Register the object itself to know the base pointer on deregistration.
- btree_insert (&registered_frames, (uintptr_type) begin, 1, ob);
-
- // Register the frame in the b-tree
- uintptr_type range[2];
- get_pc_range (ob, range);
- btree_insert (&registered_frames, range[0], range[1] - range[0], ob);
+ register_pc_range_for_object ((uintptr_type) begin, ob);
#else
init_object_mutex_once ();
__gthread_mutex_lock (&object_mutex);
@@ -232,7 +237,7 @@ __deregister_frame_info_bases (const void *begin)
#ifdef ATOMIC_FDE_FAST_PATH
// Find the originally registered object to get the base pointer.
- ob = btree_remove (&registered_frames, (uintptr_type) begin);
+ ob = btree_remove (&registered_objects, (uintptr_type) begin);
// Remove the corresponding PC range.
if (ob)
@@ -240,7 +245,7 @@ __deregister_frame_info_bases (const void *begin)
uintptr_type range[2];
get_pc_range (ob, range);
if (range[0] != range[1])
- btree_remove (&registered_frames, range[0]);
+ btree_remove (&registered_frames, range[0]);
}
// Deallocate the sort array if any.

View File

@ -0,0 +1,299 @@
commit 21109b37e8585a7a1b27650fcbf1749380016108
Author: Jakub Jelinek <jakub@redhat.com>
Date: Mon Mar 10 10:34:00 2025 +0100
libgcc: Fix up unwind-dw2-btree.h [PR119151]
The following testcase shows a bug in unwind-dw2-btree.h.
In short, the header provides lock-free btree data structure (so no parent
link on nodes, both insertion and deletion are done in top-down walks
with some locking of just a few nodes at a time so that lookups can notice
concurrent modifications and retry, non-leaf (inner) nodes contain keys
which are initially the base address of the left-most leaf entry of the
following child (or all ones if there is none) minus one, insertion ensures
balancing of the tree to ensure [d/2, d] entries filled through aggressive
splitting if it sees a full tree while walking, deletion performs various
operations like merging neighbour trees, merging into parent or moving some
nodes from neighbour to the current one).
What differs from the textbook implementations is mostly that the leaf nodes
don't include just address as a key, but address range, address + size
(where we don't insert any ranges with zero size) and the lookups can be
performed for any address in the [address, address + size) range. The keys
on inner nodes are still just address-1, so the child covers all nodes
where addr <= key unless it is covered already in children to the left.
The user (static executables or JIT) should always ensure there is no
overlap in between any of the ranges.
In the testcase a bunch of insertions are done, always followed by one
removal, followed by one insertion of a range slightly different from the
removed one. E.g. in the first case [&code[0x50], &code[0x59]] range
is removed and then we insert [&code[0x4c], &code[0x53]] range instead.
This is valid, it doesn't overlap anything. But the problem is that some
non-leaf (inner) one used the &code[0x4f] key (after the 11 insertions
completely correctly). On removal, nothing adjusts the keys on the parent
nodes (it really can't in the top-down only walk, the keys could be many nodes
above it and unlike insertion, removal only knows the start address, doesn't
know the removed size and so will discover it only when reaching the leaf
node which contains it; plus even if it knew the address and size, it still
doesn't know what the second left-most leaf node will be (i.e. the one after
removal)). And on insertion, if nodes aren't split at a level, nothing
adjusts the inner keys either. If a range is inserted and is either fully
bellow key (keys are - 1, so having address + size - 1 being equal to key is
fine) or fully after key (i.e. address > key), it works just fine, but if
the key is in a middle of the range like in this case, &code[0x4f] is in the
middle of the [&code[0x4c], &code[0x53]] range, then insertion works fine
(we only use size on the leaf nodes), and lookup of the addresses below
the key work fine too (i.e. [&code[0x4c], &code[0x4f]] will succeed).
The problem is with lookups after the key (i.e. [&code[0x50, &code[0x53]]),
the lookup looks for them in different children of the btree and doesn't
find an entry and returns NULL.
As users need to ensure non-overlapping entries at any time, the following
patch fixes it by adjusting keys during insertion where we know not just
the address but also size; if we find during the top-down walk a key
which is in the middle of the range being inserted, we simply increase the
key to be equal to address + size - 1 of the range being inserted.
There can't be any existing leaf nodes overlapping the range in correct
programs and the btree rebalancing done on deletion ensures we don't have
any empty nodes which would also cause problems.
The patch adjusts the keys in two spots, once for the current node being
walked (the last hunk in the header, with large comment trying to explain
it) and once during inner node splitting in a parent node if we'd otherwise
try to add that key in the middle of the range being inserted into the
parent node (in that case it would be missed in the last hunk).
The testcase covers both of those spots, so succeeds with GCC 12 (which
didn't have btrees) and fails with vanilla GCC trunk and also fails if
either the
if (fence < base + size - 1)
fence = iter->content.children[slot].separator = base + size - 1;
or
if (left_fence >= target && left_fence < target + size - 1)
left_fence = target + size - 1;
hunk is removed (of course, only with the current node sizes, i.e. up to
15 children of inner nodes and up to 10 entries in leaf nodes).
2025-03-10 Jakub Jelinek <jakub@redhat.com>
Michael Leuchtenburg <michael@slashhome.org>
PR libgcc/119151
* unwind-dw2-btree.h (btree_split_inner): Add size argument. If
left_fence is in the middle of [target,target + size - 1] range,
increase it to target + size - 1.
(btree_insert): Adjust btree_split_inner caller. If fence is smaller
than base + size - 1, increase it and separator of the slot to
base + size - 1.
* gcc.dg/pr119151.c: New test.
diff --git a/gcc/testsuite/gcc.dg/pr119151.c b/gcc/testsuite/gcc.dg/pr119151.c
new file mode 100644
index 0000000000000000..6ef0f12ce9ae6c06
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr119151.c
@@ -0,0 +1,151 @@
+/* PR libgcc/119151 */
+/* Should be run just on targets which don't have _Unwind_Find_FDE in libc.so. */
+/* { dg-do run { target { { x86_64-*-linux* aarch64*-*-linux* powerpc64*-*-linux* riscv*-*-linux* } && lp64 } } } */
+/* { dg-options "-O2" } */
+
+struct object
+{
+ void *pc_begin, *tbase, *dbase, *single;
+ __SIZE_TYPE__ i;
+ void *fde_end, *next;
+};
+struct dwarf_eh_bases
+{
+ void *tbase, *dbase, *func;
+};
+extern void __register_frame_info (const void *, struct object *);
+extern void *__deregister_frame_info (const void *);
+extern const void *_Unwind_Find_FDE (void *, struct dwarf_eh_bases *);
+#define DW_EH_PE_sdata8 0x0c
+#define DW_EH_PE_pcrel 0x10
+#define DW_CFA_def_cfa 0x0c
+#define DW_CFA_offset 0x80
+
+struct __attribute__((aligned (8))) eh_frame_cie {
+ unsigned len;
+ unsigned tag;
+ unsigned char version;
+ unsigned char augmentation[3];
+ unsigned char code_align_factor;
+ unsigned char data_align_factor;
+ unsigned char ra_column;
+ unsigned char augmentation_size;
+ unsigned char encoding;
+ unsigned char def_cfa;
+ unsigned char def_cfa_op1, def_cfa_op2;
+ unsigned char offset;
+ unsigned char offset_op;
+};
+struct __attribute__((aligned (8))) eh_frame_fde {
+ unsigned len;
+ unsigned cie_offset;
+ unsigned long long begin, size;
+ unsigned char augmentation;
+};
+struct eh_frame_cie_fde {
+ struct eh_frame_cie cie;
+ struct eh_frame_fde fde;
+ unsigned int zero;
+ struct object obj;
+} eh_frame[256];
+unsigned ehidx;
+unsigned char code[0x800] __attribute__((aligned (8)));
+
+void *
+register_range (void *addr, unsigned size)
+{
+ /* Fills in empty-ish CIE and FDE with pcrel sdata8 encoding so that
+ we don't need to worry about lp64 large code models.
+ We don't actually execute anything in code and only _Unwind_Find_FDE,
+ don't actually try to unwind anything. */
+ eh_frame[ehidx].cie.len
+ = (unsigned) ((char *) &eh_frame[ehidx].fde
+ - (char *) &eh_frame[ehidx].cie.tag);
+ eh_frame[ehidx].cie.tag = 0;
+ eh_frame[ehidx].cie.version = 3;
+ __builtin_memcpy (eh_frame[ehidx].cie.augmentation, "zR", 3);
+ eh_frame[ehidx].cie.code_align_factor = 1;
+ eh_frame[ehidx].cie.data_align_factor = 0x78; /* sleb128 -8 */
+ eh_frame[ehidx].cie.ra_column = 0x10;
+ eh_frame[ehidx].cie.augmentation_size = 1;
+ eh_frame[ehidx].cie.encoding = DW_EH_PE_pcrel | DW_EH_PE_sdata8;
+ eh_frame[ehidx].cie.def_cfa = DW_CFA_def_cfa;
+ eh_frame[ehidx].cie.def_cfa_op1 = 7;
+ eh_frame[ehidx].cie.def_cfa_op2 = 8;
+ eh_frame[ehidx].cie.offset = DW_CFA_offset + 0x10;
+ eh_frame[ehidx].cie.offset_op = 1;
+ eh_frame[ehidx].fde.len
+ = (unsigned) ((char *) &eh_frame[ehidx].zero
+ - (char *) &eh_frame[ehidx].fde.cie_offset);
+ eh_frame[ehidx].fde.cie_offset
+ = (unsigned) ((char *) &eh_frame[ehidx].fde.cie_offset
+ - (char *) &eh_frame[ehidx].cie);
+ eh_frame[ehidx].fde.begin
+ = (__INTPTR_TYPE__) ((__UINTPTR_TYPE__) addr
+ - (__UINTPTR_TYPE__) &eh_frame[ehidx].fde.begin);
+ eh_frame[ehidx].fde.size = size;
+ eh_frame[ehidx].fde.augmentation = 0;
+ eh_frame[ehidx].zero = 0;
+ __register_frame_info (&eh_frame[ehidx].cie, &eh_frame[ehidx].obj);
+ ++ehidx;
+ return &eh_frame[ehidx - 1].cie;
+}
+
+void
+unregister (void *eh_frame)
+{
+ __deregister_frame_info (eh_frame);
+}
+
+int
+main ()
+{
+ for (int i = 0; i < 0x50; i += 0x10)
+ register_range (&code[i], 10);
+ void *p = register_range (&code[0x50], 10);
+ for (int i = 0x60; i < 0xb0; i += 0x10)
+ register_range (&code[i], 10);
+ unregister (p);
+ register_range (&code[0x4c], 8);
+ struct dwarf_eh_bases bases;
+ const void *q = _Unwind_Find_FDE (&code[0x4c], &bases);
+ const void *r = _Unwind_Find_FDE (&code[0x51], &bases);
+ if (!q || q != r)
+ __builtin_abort ();
+ for (int i = 0; i <= 0xa0; i += 0x10)
+ if (i != 0x50)
+ {
+ q = _Unwind_Find_FDE (&code[i], &bases);
+ r = _Unwind_Find_FDE (&code[i + 9], &bases);
+ if (!q || q != r)
+ __builtin_abort ();
+ }
+ for (int i = 0xb0; i < 0x240; i += 0x10)
+ register_range (&code[i], 10);
+ p = register_range (&code[0x240], 10);
+ for (int i = 0x250; i < 0x470; i += 0x10)
+ register_range (&code[i], 10);
+ void *s = register_range (&code[0x470], 10);
+ for (int i = 0x480; i < 0x700; i += 0x10)
+ register_range (&code[i], 10);
+ unregister (p);
+ register_range (&code[0x23c], 16);
+ q = _Unwind_Find_FDE (&code[0x23d], &bases);
+ r = _Unwind_Find_FDE (&code[0x24b], &bases);
+ if (!q || q != r)
+ __builtin_abort ();
+ unregister (s);
+ register_range (&code[0x46c], 16);
+ q = _Unwind_Find_FDE (&code[0x46d], &bases);
+ r = _Unwind_Find_FDE (&code[0x47b], &bases);
+ if (!q || q != r)
+ __builtin_abort ();
+ for (int i = 0; i < 0x700; i += 0x10)
+ if (i != 0x50 && i != 0x240 && i != 0x470)
+ {
+ q = _Unwind_Find_FDE (&code[i], &bases);
+ r = _Unwind_Find_FDE (&code[i + 9], &bases);
+ if (!q || q != r)
+ __builtin_abort ();
+ }
+}
diff --git a/libgcc/unwind-dw2-btree.h b/libgcc/unwind-dw2-btree.h
index ace507d9ffbdffb7..e3f3a11a7b5443d6 100644
--- a/libgcc/unwind-dw2-btree.h
+++ b/libgcc/unwind-dw2-btree.h
@@ -474,7 +474,8 @@ btree_handle_root_split (struct btree *t, struct btree_node **node,
// Split an inner node.
static void
btree_split_inner (struct btree *t, struct btree_node **inner,
- struct btree_node **parent, uintptr_type target)
+ struct btree_node **parent, uintptr_type target,
+ uintptr_type size)
{
// Check for the root.
btree_handle_root_split (t, inner, parent);
@@ -490,6 +491,9 @@ btree_split_inner (struct btree *t, struct btree_node **inner,
= left_inner->content.children[split + index];
left_inner->entry_count = split;
uintptr_type left_fence = btree_node_get_fence_key (left_inner);
+ if (left_fence >= target && left_fence < target + size - 1)
+ // See the PR119151 comment in btree_insert.
+ left_fence = target + size - 1;
btree_node_update_separator_after_split (*parent, right_fence, left_fence,
right_inner);
if (target <= left_fence)
@@ -753,13 +757,28 @@ btree_insert (struct btree *t, uintptr_type base, uintptr_type size,
{
// Use eager splits to avoid lock coupling up.
if (iter->entry_count == max_fanout_inner)
- btree_split_inner (t, &iter, &parent, base);
+ btree_split_inner (t, &iter, &parent, base, size);
unsigned slot = btree_node_find_inner_slot (iter, base);
if (parent)
btree_node_unlock_exclusive (parent);
parent = iter;
fence = iter->content.children[slot].separator;
+ if (fence < base + size - 1)
+ // The separator was set to the base - 1 of the leftmost leaf child
+ // at some point but such an entry could have been removed afterwards.
+ // As both insertion and removal are just walking down the tree with
+ // only a few current nodes locked at a time, updating the separator
+ // on removal is not possible, especially because btree_remove does
+ // not know the size until it reaches leaf node. We must ensure that
+ // the separator is not in a middle of some entry though, as
+ // btree_lookup can look up any address in the entry's range and if
+ // the separator is in the middle, addresses below it or equal to it
+ // would be found while addresses above it would result in failed
+ // lookup. Update the separator now. Assumption that users
+ // ensure no overlapping registered ranges, there should be no
+ // current entry for any address in the range. See PR119151.
+ fence = iter->content.children[slot].separator = base + size - 1;
iter = iter->content.children[slot].child;
btree_node_lock_exclusive (iter);
}

View File

@ -0,0 +1,204 @@
commit 9488d24206687be80443dafdb2cdfc4ff3aca28c
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Nov 25 18:40:51 2021 +0100
libgcc: Split FDE search code from PT_GNU_EH_FRAME lookup
This allows switching to a different implementation for
PT_GNU_EH_FRAME lookup in a subsequent commit.
This moves some of the PT_GNU_EH_FRAME parsing out of the glibc loader
lock that is implied by dl_iterate_phdr. However, the FDE is already
parsed outside the lock before this change, so this does not introduce
additional crashes in case of a concurrent dlclose.
libgcc/ChangeLog:
* unwind-dw2-fde-dip.c (struct unw_eh_callback_data): Add hdr.
Remove func, ret.
(find_fde_tail): New function. Split from
_Unwind_IteratePhdrCallback. Move the result initialization
from _Unwind_Find_FDE.
(_Unwind_Find_FDE): Updated to call find_fde_tail.
diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index 3f302826d2d49074..fbb0fbdebb92d484 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -113,8 +113,7 @@ struct unw_eh_callback_data
#if NEED_DBASE_MEMBER
void *dbase;
#endif
- void *func;
- const fde *ret;
+ const struct unw_eh_frame_hdr *hdr;
int check_cache;
};
@@ -197,10 +196,6 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
#else
_Unwind_Ptr load_base;
#endif
- const unsigned char *p;
- const struct unw_eh_frame_hdr *hdr;
- _Unwind_Ptr eh_frame;
- struct object ob;
_Unwind_Ptr pc_low = 0, pc_high = 0;
struct ext_dl_phdr_info
@@ -348,10 +343,8 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
return 0;
/* Read .eh_frame_hdr header. */
- hdr = (const struct unw_eh_frame_hdr *)
+ data->hdr = (const struct unw_eh_frame_hdr *)
__RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
- if (hdr->version != 1)
- return 1;
#ifdef CRT_GET_RFIB_DATA
# if defined __i386__ || defined __nios2__
@@ -383,12 +376,30 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
# endif
#endif
- _Unwind_Ptr dbase = unw_eh_callback_data_dbase (data);
+ return 1;
+}
+
+/* Find the FDE for the program counter PC, in a previously located
+ PT_GNU_EH_FRAME data region. *BASES is updated if an FDE to return is
+ found. */
+
+static const fde *
+find_fde_tail (_Unwind_Ptr pc,
+ const struct unw_eh_frame_hdr *hdr,
+ _Unwind_Ptr dbase,
+ struct dwarf_eh_bases *bases)
+{
+ const unsigned char *p = (const unsigned char *) (hdr + 1);
+ _Unwind_Ptr eh_frame;
+ struct object ob;
+
+ if (hdr->version != 1)
+ return NULL;
+
p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
base_from_cb_data (hdr->eh_frame_ptr_enc,
dbase),
- (const unsigned char *) (hdr + 1),
- &eh_frame);
+ p, &eh_frame);
/* We require here specific table encoding to speed things up.
Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
@@ -404,7 +415,7 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
p, &fde_count);
/* Shouldn't happen. */
if (fde_count == 0)
- return 1;
+ return NULL;
if ((((_Unwind_Ptr) p) & 3) == 0)
{
struct fde_table {
@@ -419,9 +430,9 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
_Unwind_Ptr range;
mid = fde_count - 1;
- if (data->pc < table[0].initial_loc + data_base)
- return 1;
- else if (data->pc < table[mid].initial_loc + data_base)
+ if (pc < table[0].initial_loc + data_base)
+ return NULL;
+ else if (pc < table[mid].initial_loc + data_base)
{
lo = 0;
hi = mid;
@@ -429,9 +440,9 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
while (lo < hi)
{
mid = (lo + hi) / 2;
- if (data->pc < table[mid].initial_loc + data_base)
+ if (pc < table[mid].initial_loc + data_base)
hi = mid;
- else if (data->pc >= table[mid + 1].initial_loc + data_base)
+ else if (pc >= table[mid + 1].initial_loc + data_base)
lo = mid + 1;
else
break;
@@ -445,10 +456,16 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
f_enc_size = size_of_encoded_value (f_enc);
read_encoded_value_with_base (f_enc & 0x0f, 0,
&f->pc_begin[f_enc_size], &range);
- if (data->pc < table[mid].initial_loc + data_base + range)
- data->ret = f;
- data->func = (void *) (table[mid].initial_loc + data_base);
- return 1;
+ _Unwind_Ptr func = table[mid].initial_loc + data_base;
+ if (pc < table[mid].initial_loc + data_base + range)
+ {
+ bases->tbase = NULL;
+ bases->dbase = (void *) dbase;
+ bases->func = (void *) func;
+ return f;
+ }
+ else
+ return NULL;
}
}
@@ -461,18 +478,20 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
ob.u.single = (fde *) eh_frame;
ob.s.i = 0;
ob.s.b.mixed_encoding = 1; /* Need to assume worst case. */
- data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
- if (data->ret != NULL)
+ const fde *entry = linear_search_fdes (&ob, (fde *) eh_frame, (void *) pc);
+ if (entry != NULL)
{
_Unwind_Ptr func;
- unsigned int encoding = get_fde_encoding (data->ret);
+ unsigned int encoding = get_fde_encoding (entry);
read_encoded_value_with_base (encoding,
base_from_cb_data (encoding, dbase),
- data->ret->pc_begin, &func);
- data->func = (void *) func;
+ entry->pc_begin, &func);
+ bases->tbase = NULL;
+ bases->dbase = (void *) dbase;
+ bases->func = (void *) func;
}
- return 1;
+ return entry;
}
const fde *
@@ -489,24 +508,13 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
#if NEED_DBASE_MEMBER
data.dbase = NULL;
#endif
- data.func = NULL;
- data.ret = NULL;
data.check_cache = 1;
- if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
+ if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) <= 0)
return NULL;
- if (data.ret)
- {
- bases->tbase = NULL;
-#if NEED_DBASE_MEMBER
- bases->dbase = data.dbase;
-#else
- bases->dbase = NULL;
-#endif
- bases->func = data.func;
- }
- return data.ret;
+ _Unwind_Ptr dbase = unw_eh_callback_data_dbase (&data);
+ return find_fde_tail ((_Unwind_Ptr) pc, data.hdr, dbase, bases);
}
#else

View File

@ -0,0 +1,40 @@
commit 790854ea7670f11c14d431c102a49181d2915965
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Jan 4 15:47:30 2022 +0100
libgcc: Use _dl_find_object in _Unwind_Find_FDE
libgcc/ChangeLog:
* unwind-dw2-fde-dip.c (_Unwind_Find_FDE): Call _dl_find_object
if available.
diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index fbb0fbdebb92d484..b837d8e490425652 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -504,6 +504,24 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
if (ret != NULL)
return ret;
+ /* Use DLFO_STRUCT_HAS_EH_DBASE as a proxy for the existence of a glibc-style
+ _dl_find_object function. */
+#ifdef DLFO_STRUCT_HAS_EH_DBASE
+ {
+ struct dl_find_object dlfo;
+ if (_dl_find_object (pc, &dlfo) == 0)
+ return find_fde_tail ((_Unwind_Ptr) pc, dlfo.dlfo_eh_frame,
+# if DLFO_STRUCT_HAS_EH_DBASE
+ (_Unwind_Ptr) dlfo.dlfo_eh_dbase,
+# else
+ NULL,
+# endif
+ bases);
+ else
+ return NULL;
+ }
+#endif /* DLFO_STRUCT_HAS_EH_DBASE */
+
data.pc = (_Unwind_Ptr) pc;
#if NEED_DBASE_MEMBER
data.dbase = NULL;

View File

@ -0,0 +1,27 @@
commit ab2a2457780d224343ce05e7d8e2964c6a47fd83
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Jan 25 12:09:56 2022 +0100
libgcc: Fix _Unwind_Find_FDE for missing unwind data with glibc 2.35
_dl_find_object returns success even if no unwind information has been
found, and dlfo_eh_frame is NULL.
libgcc/ChangeLog:
PR libgcc/104207
* unwind-dw2-fde-dip.c (_Unwind_Find_FDE): Add NULL check.
diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index b837d8e490425652..1744c91958013ebb 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -509,7 +509,7 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
#ifdef DLFO_STRUCT_HAS_EH_DBASE
{
struct dl_find_object dlfo;
- if (_dl_find_object (pc, &dlfo) == 0)
+ if (_dl_find_object (pc, &dlfo) == 0 && dlfo.dlfo_eh_frame != NULL)
return find_fde_tail ((_Unwind_Ptr) pc, dlfo.dlfo_eh_frame,
# if DLFO_STRUCT_HAS_EH_DBASE
(_Unwind_Ptr) dlfo.dlfo_eh_dbase,

View File

@ -0,0 +1,28 @@
commit 157cc4e0117756503c7c63df97cf31de7570b088
Author: Xi Ruoyao <xry111@mengyan1223.wang>
Date: Fri Feb 25 01:45:57 2022 +0800
libgcc: fix a warning calling find_fde_tail
The third parameter of find_fde_tail is an _Unwind_Ptr (which is an
integer type instead of a pointer), but we are passing NULL to it. This
causes a -Wint-conversion warning.
libgcc/
* unwind-dw2-fde-dip.c (_Unwind_Find_FDE): Call find_fde_tail
with 0 instead of NULL.
diff --git a/libgcc/unwind-dw2-fde-dip.c b/libgcc/unwind-dw2-fde-dip.c
index 1744c91958013ebb..25f2e44c5823cf64 100644
--- a/libgcc/unwind-dw2-fde-dip.c
+++ b/libgcc/unwind-dw2-fde-dip.c
@@ -514,7 +514,7 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
# if DLFO_STRUCT_HAS_EH_DBASE
(_Unwind_Ptr) dlfo.dlfo_eh_dbase,
# else
- NULL,
+ 0,
# endif
bases);
else

View File

@ -0,0 +1,222 @@
commit 0d344b557604e966dc7f91739881f03e1f221efd
Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
Date: Thu Feb 10 17:42:56 2022 +0000
aarch64: Fix pac-ret with unusual dwarf in libgcc unwinder [PR104689]
The RA_SIGN_STATE dwarf pseudo-register is normally only set using the
DW_CFA_AARCH64_negate_ra_state (== DW_CFA_window_save) operation which
toggles the return address signedness state (the default state is 0).
(It may be set by remember/restore_state CFI too, those save/restore
the state of all registers.)
However RA_SIGN_STATE can be set directly via DW_CFA_val_expression too.
GCC does not generate such CFI but some other compilers reportedly do.
Note: the toggle operation must not be mixed with other dwarf register
rule CFI within the same CIE and FDE.
In libgcc we assume REG_UNSAVED means the RA_STATE is set using toggle
operations, otherwise we assume its value is set by other CFI.
libgcc/ChangeLog:
PR target/104689
* config/aarch64/aarch64-unwind.h (aarch64_frob_update_context):
Handle the !REG_UNSAVED case.
* unwind-dw2.c (execute_cfa_program): Fail toggle if !REG_UNSAVED.
gcc/testsuite/ChangeLog:
PR target/104689
* gcc.target/aarch64/pr104689.c: New test.
diff --git a/gcc/testsuite/gcc.target/aarch64/pr104689.c b/gcc/testsuite/gcc.target/aarch64/pr104689.c
new file mode 100644
index 0000000000000000..3b7adbdfe7d6f969
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr104689.c
@@ -0,0 +1,149 @@
+/* PR target/104689. Unwind across pac-ret frames with unusual dwarf. */
+/* { dg-do run } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-fexceptions -O2" } */
+
+#include <unwind.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define die() \
+ do { \
+ printf ("%s:%d: reached unexpectedly.\n", __FILE__, __LINE__); \
+ fflush (stdout); \
+ abort (); \
+ } while (0)
+
+
+/* Code to invoke unwinding with a logging callback. */
+
+static struct _Unwind_Exception exc;
+
+static _Unwind_Reason_Code
+force_unwind_stop (int version, _Unwind_Action actions,
+ _Unwind_Exception_Class exc_class,
+ struct _Unwind_Exception *exc_obj,
+ struct _Unwind_Context *context,
+ void *stop_parameter)
+{
+ printf ("%s: CFA: %p PC: %p actions: %d\n",
+ __func__,
+ (void *)_Unwind_GetCFA (context),
+ (void *)_Unwind_GetIP (context),
+ (int)actions);
+ if (actions & _UA_END_OF_STACK)
+ die ();
+ return _URC_NO_REASON;
+}
+
+static void force_unwind (void)
+{
+#ifndef __USING_SJLJ_EXCEPTIONS__
+ _Unwind_ForcedUnwind (&exc, force_unwind_stop, 0);
+#else
+ _Unwind_SjLj_ForcedUnwind (&exc, force_unwind_stop, 0);
+#endif
+}
+
+
+/* Define functions with unusual pac-ret dwarf via top level asm. */
+
+#define STR(x) #x
+#define DW_CFA_val_expression 0x16
+#define RA_SIGN_STATE 34
+#define DW_OP_lit0 0x30
+#define DW_OP_lit1 0x31
+
+#define cfi_escape(a1, a2, a3, a4) \
+ ".cfi_escape " STR(a1) ", " STR(a2) ", " STR(a3) ", " STR(a4)
+
+/* Bytes: 0x16 0x22 0x01 0x30 */
+#define SET_RA_STATE_0 \
+ cfi_escape (DW_CFA_val_expression, RA_SIGN_STATE, 1, DW_OP_lit0)
+
+/* Bytes: 0x16 0x22 0x01 0x31 */
+#define SET_RA_STATE_1 \
+ cfi_escape (DW_CFA_val_expression, RA_SIGN_STATE, 1, DW_OP_lit1)
+
+/* These function call their argument. */
+void unusual_pac_ret (void *);
+void unusual_no_pac_ret (void *);
+
+asm(""
+".global unusual_pac_ret\n"
+".type unusual_pac_ret, %function\n"
+"unusual_pac_ret:\n"
+" .cfi_startproc\n"
+" " SET_RA_STATE_0 "\n"
+" hint 25 // paciasp\n"
+" " SET_RA_STATE_1 "\n"
+" stp x29, x30, [sp, -16]!\n"
+" .cfi_def_cfa_offset 16\n"
+" .cfi_offset 29, -16\n"
+" .cfi_offset 30, -8\n"
+" mov x29, sp\n"
+" blr x0\n"
+" ldp x29, x30, [sp], 16\n"
+" .cfi_restore 30\n"
+" .cfi_restore 29\n"
+" .cfi_def_cfa_offset 0\n"
+" hint 29 // autiasp\n"
+" " SET_RA_STATE_0 "\n"
+" ret\n"
+" .cfi_endproc\n");
+
+asm(""
+".global unusual_no_pac_ret\n"
+".type unusual_no_pac_ret, %function\n"
+"unusual_no_pac_ret:\n"
+" .cfi_startproc\n"
+" " SET_RA_STATE_0 "\n"
+" stp x29, x30, [sp, -16]!\n"
+" .cfi_def_cfa_offset 16\n"
+" .cfi_offset 29, -16\n"
+" .cfi_offset 30, -8\n"
+" mov x29, sp\n"
+" blr x0\n"
+" ldp x29, x30, [sp], 16\n"
+" .cfi_restore 30\n"
+" .cfi_restore 29\n"
+" .cfi_def_cfa_offset 0\n"
+" ret\n"
+" .cfi_endproc\n");
+
+
+/* Functions to create a call chain with mixed pac-ret dwarf. */
+
+__attribute__((target("branch-protection=pac-ret")))
+static void f2_pac_ret (void)
+{
+ force_unwind ();
+ die ();
+}
+
+__attribute__((target("branch-protection=none")))
+static void f1_no_pac_ret (void)
+{
+ unusual_pac_ret (f2_pac_ret);
+ die ();
+}
+
+__attribute__((noinline, target("branch-protection=pac-ret")))
+static void f0_pac_ret (void)
+{
+ unusual_no_pac_ret (f1_no_pac_ret);
+ die ();
+}
+
+static void cleanup_handler (void *p)
+{
+ printf ("%s: Success.\n", __func__);
+ exit (0);
+}
+
+int main ()
+{
+ char dummy __attribute__((cleanup (cleanup_handler)));
+ f0_pac_ret ();
+ die ();
+}
diff --git a/libgcc/config/aarch64/aarch64-unwind.h b/libgcc/config/aarch64/aarch64-unwind.h
index b6faa9b495094d8e..3158af4c8c371fac 100644
--- a/libgcc/config/aarch64/aarch64-unwind.h
+++ b/libgcc/config/aarch64/aarch64-unwind.h
@@ -78,7 +78,13 @@ static inline void
aarch64_frob_update_context (struct _Unwind_Context *context,
_Unwind_FrameState *fs)
{
- if (fs->regs.reg[DWARF_REGNUM_AARCH64_RA_STATE].loc.offset & 0x1)
+ const int reg = DWARF_REGNUM_AARCH64_RA_STATE;
+ int ra_signed;
+ if (fs->regs.reg[reg].how == REG_UNSAVED)
+ ra_signed = fs->regs.reg[reg].loc.offset & 0x1;
+ else
+ ra_signed = _Unwind_GetGR (context, reg) & 0x1;
+ if (ra_signed)
/* The flag is used for re-authenticating EH handler's address. */
context->flags |= RA_SIGNED_BIT;
else
diff --git a/libgcc/unwind-dw2.c b/libgcc/unwind-dw2.c
index 41af7e23f47602ec..43d06531fce9bed1 100644
--- a/libgcc/unwind-dw2.c
+++ b/libgcc/unwind-dw2.c
@@ -1204,7 +1204,9 @@ execute_cfa_program (const unsigned char *insn_ptr,
#if defined (__aarch64__) && !defined (__ILP32__)
/* This CFA is multiplexed with Sparc. On AArch64 it's used to toggle
return address signing status. */
- fs->regs.reg[DWARF_REGNUM_AARCH64_RA_STATE].loc.offset ^= 1;
+ reg = DWARF_REGNUM_AARCH64_RA_STATE;
+ gcc_assert (fs->regs.reg[reg].how == REG_UNSAVED);
+ fs->regs.reg[reg].loc.offset ^= 1;
#else
/* ??? Hardcoded for SPARC register window configuration. */
if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,377 @@
commit d458f806afe07d1e06bdf275e94d05a716f41bf6
Author: Thomas Neumann <tneumann@users.sourceforge.net>
Date: Sun Sep 18 11:31:01 2022 +0200
Remove dependency on uintptr_t in libgcc
uintptr_t is no available for all targets, use __UINTPTR_TYPE__
instead.
libgcc/ChangeLog:
* unwind-dw2-fde.c: Replace uintptr_t with typedef
for __UINTPTR_TYPE__.
* unwind-dw2-btree.h: Likewise.
diff --git a/libgcc/unwind-dw2-btree.h b/libgcc/unwind-dw2-btree.h
index 8853f0eab486b847..ace507d9ffbdffb7 100644
--- a/libgcc/unwind-dw2-btree.h
+++ b/libgcc/unwind-dw2-btree.h
@@ -39,7 +39,7 @@ struct version_lock
// range. Even on 32 bit platforms that would require 1 billion
// frame registrations within the time span of a few assembler
// instructions.
- uintptr_t version_lock;
+ uintptr_type version_lock;
};
#ifdef __GTHREAD_HAS_COND
@@ -60,7 +60,7 @@ version_lock_initialize_locked_exclusive (struct version_lock *vl)
static inline bool
version_lock_try_lock_exclusive (struct version_lock *vl)
{
- uintptr_t state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
+ uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
if (state & 1)
return false;
return __atomic_compare_exchange_n (&(vl->version_lock), &state, state | 1,
@@ -78,7 +78,7 @@ restart:
// We should virtually never get contention here, as frame
// changes are rare.
- uintptr_t state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
+ uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
if (!(state & 1))
{
if (__atomic_compare_exchange_n (&(vl->version_lock), &state, state | 1,
@@ -134,8 +134,8 @@ static void
version_lock_unlock_exclusive (struct version_lock *vl)
{
// increase version, reset exclusive lock bits
- uintptr_t state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
- uintptr_t ns = (state + 4) & (~((uintptr_t) 3));
+ uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
+ uintptr_type ns = (state + 4) & (~((uintptr_type) 3));
state = __atomic_exchange_n (&(vl->version_lock), ns, __ATOMIC_SEQ_CST);
#ifdef __GTHREAD_HAS_COND
@@ -152,9 +152,9 @@ version_lock_unlock_exclusive (struct version_lock *vl)
// Acquire an optimistic "lock". Note that this does not lock at all, it
// only allows for validation later.
static inline bool
-version_lock_lock_optimistic (const struct version_lock *vl, uintptr_t *lock)
+version_lock_lock_optimistic (const struct version_lock *vl, uintptr_type *lock)
{
- uintptr_t state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
+ uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
*lock = state;
// Acquiring the lock fails when there is currently an exclusive lock.
@@ -163,7 +163,7 @@ version_lock_lock_optimistic (const struct version_lock *vl, uintptr_t *lock)
// Validate a previously acquired "lock".
static inline bool
-version_lock_validate (const struct version_lock *vl, uintptr_t lock)
+version_lock_validate (const struct version_lock *vl, uintptr_type lock)
{
// Prevent the reordering of non-atomic loads behind the atomic load.
// Hans Boehm, Can Seqlocks Get Along with Programming Language Memory
@@ -171,26 +171,26 @@ version_lock_validate (const struct version_lock *vl, uintptr_t lock)
__atomic_thread_fence (__ATOMIC_ACQUIRE);
// Check that the node is still in the same state.
- uintptr_t state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
+ uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST);
return (state == lock);
}
// The largest possible separator value.
-static const uintptr_t max_separator = ~((uintptr_t) (0));
+static const uintptr_type max_separator = ~((uintptr_type) (0));
struct btree_node;
// Inner entry. The child tree contains all entries <= separator.
struct inner_entry
{
- uintptr_t separator;
+ uintptr_type separator;
struct btree_node *child;
};
// Leaf entry. Stores an object entry.
struct leaf_entry
{
- uintptr_t base, size;
+ uintptr_type base, size;
struct object *ob;
};
@@ -248,7 +248,7 @@ btree_node_needs_merge (const struct btree_node *n)
}
// Get the fence key for inner nodes.
-static inline uintptr_t
+static inline uintptr_type
btree_node_get_fence_key (const struct btree_node *n)
{
// For inner nodes we just return our right-most entry.
@@ -257,7 +257,7 @@ btree_node_get_fence_key (const struct btree_node *n)
// Find the position for a slot in an inner node.
static unsigned
-btree_node_find_inner_slot (const struct btree_node *n, uintptr_t value)
+btree_node_find_inner_slot (const struct btree_node *n, uintptr_type value)
{
for (unsigned index = 0, ec = n->entry_count; index != ec; ++index)
if (n->content.children[index].separator >= value)
@@ -267,7 +267,7 @@ btree_node_find_inner_slot (const struct btree_node *n, uintptr_t value)
// Find the position for a slot in a leaf node.
static unsigned
-btree_node_find_leaf_slot (const struct btree_node *n, uintptr_t value)
+btree_node_find_leaf_slot (const struct btree_node *n, uintptr_type value)
{
for (unsigned index = 0, ec = n->entry_count; index != ec; ++index)
if (n->content.entries[index].base + n->content.entries[index].size > value)
@@ -299,14 +299,14 @@ btree_node_unlock_exclusive (struct btree_node *n)
// Acquire an optimistic "lock". Note that this does not lock at all, it
// only allows for validation later.
static inline bool
-btree_node_lock_optimistic (const struct btree_node *n, uintptr_t *lock)
+btree_node_lock_optimistic (const struct btree_node *n, uintptr_type *lock)
{
return version_lock_lock_optimistic (&(n->version_lock), lock);
}
// Validate a previously acquire lock.
static inline bool
-btree_node_validate (const struct btree_node *n, uintptr_t lock)
+btree_node_validate (const struct btree_node *n, uintptr_type lock)
{
return version_lock_validate (&(n->version_lock), lock);
}
@@ -314,8 +314,8 @@ btree_node_validate (const struct btree_node *n, uintptr_t lock)
// Insert a new separator after splitting.
static void
btree_node_update_separator_after_split (struct btree_node *n,
- uintptr_t old_separator,
- uintptr_t new_separator,
+ uintptr_type old_separator,
+ uintptr_type new_separator,
struct btree_node *new_right)
{
unsigned slot = btree_node_find_inner_slot (n, old_separator);
@@ -474,13 +474,13 @@ btree_handle_root_split (struct btree *t, struct btree_node **node,
// Split an inner node.
static void
btree_split_inner (struct btree *t, struct btree_node **inner,
- struct btree_node **parent, uintptr_t target)
+ struct btree_node **parent, uintptr_type target)
{
// Check for the root.
btree_handle_root_split (t, inner, parent);
// Create two inner node.
- uintptr_t right_fence = btree_node_get_fence_key (*inner);
+ uintptr_type right_fence = btree_node_get_fence_key (*inner);
struct btree_node *left_inner = *inner;
struct btree_node *right_inner = btree_allocate_node (t, true);
unsigned split = left_inner->entry_count / 2;
@@ -489,7 +489,7 @@ btree_split_inner (struct btree *t, struct btree_node **inner,
right_inner->content.children[index]
= left_inner->content.children[split + index];
left_inner->entry_count = split;
- uintptr_t left_fence = btree_node_get_fence_key (left_inner);
+ uintptr_type left_fence = btree_node_get_fence_key (left_inner);
btree_node_update_separator_after_split (*parent, right_fence, left_fence,
right_inner);
if (target <= left_fence)
@@ -507,13 +507,14 @@ btree_split_inner (struct btree *t, struct btree_node **inner,
// Split a leaf node.
static void
btree_split_leaf (struct btree *t, struct btree_node **leaf,
- struct btree_node **parent, uintptr_t fence, uintptr_t target)
+ struct btree_node **parent, uintptr_type fence,
+ uintptr_type target)
{
// Check for the root.
btree_handle_root_split (t, leaf, parent);
// Create two leaf nodes.
- uintptr_t right_fence = fence;
+ uintptr_type right_fence = fence;
struct btree_node *left_leaf = *leaf;
struct btree_node *right_leaf = btree_allocate_node (t, false);
unsigned split = left_leaf->entry_count / 2;
@@ -522,7 +523,7 @@ btree_split_leaf (struct btree *t, struct btree_node **leaf,
right_leaf->content.entries[index]
= left_leaf->content.entries[split + index];
left_leaf->entry_count = split;
- uintptr_t left_fence = right_leaf->content.entries[0].base - 1;
+ uintptr_type left_fence = right_leaf->content.entries[0].base - 1;
btree_node_update_separator_after_split (*parent, right_fence, left_fence,
right_leaf);
if (target <= left_fence)
@@ -540,7 +541,7 @@ btree_split_leaf (struct btree *t, struct btree_node **leaf,
// Merge (or balance) child nodes.
static struct btree_node *
btree_merge_node (struct btree *t, unsigned child_slot,
- struct btree_node *parent, uintptr_t target)
+ struct btree_node *parent, uintptr_type target)
{
// Choose the emptiest neighbor and lock both. The target child is already
// locked.
@@ -693,7 +694,7 @@ btree_merge_node (struct btree *t, unsigned child_slot,
left_node->entry_count += to_shift;
right_node->entry_count -= to_shift;
}
- uintptr_t left_fence;
+ uintptr_type left_fence;
if (btree_node_is_leaf (left_node))
{
left_fence = right_node->content.entries[0].base - 1;
@@ -718,7 +719,7 @@ btree_merge_node (struct btree *t, unsigned child_slot,
// Insert an entry.
static bool
-btree_insert (struct btree *t, uintptr_t base, uintptr_t size,
+btree_insert (struct btree *t, uintptr_type base, uintptr_type size,
struct object *ob)
{
// Sanity check.
@@ -747,7 +748,7 @@ btree_insert (struct btree *t, uintptr_t base, uintptr_t size,
// But that is more difficult to implement and frame registration is
// rare anyway, we use simple locking for now.
- uintptr_t fence = max_separator;
+ uintptr_type fence = max_separator;
while (btree_node_is_inner (iter))
{
// Use eager splits to avoid lock coupling up.
@@ -790,7 +791,7 @@ btree_insert (struct btree *t, uintptr_t base, uintptr_t size,
// Remove an entry.
static struct object *
-btree_remove (struct btree *t, uintptr_t base)
+btree_remove (struct btree *t, uintptr_type base)
{
// Access the root.
version_lock_lock_exclusive (&(t->root_lock));
@@ -838,7 +839,7 @@ btree_remove (struct btree *t, uintptr_t base)
// Find the corresponding entry for the given address.
static struct object *
-btree_lookup (const struct btree *t, uintptr_t target_addr)
+btree_lookup (const struct btree *t, uintptr_type target_addr)
{
// Within this function many loads are relaxed atomic loads.
// Use a macro to keep the code reasonable.
@@ -867,7 +868,7 @@ btree_lookup (const struct btree *t, uintptr_t target_addr)
restart:
struct btree_node *iter;
- uintptr_t lock;
+ uintptr_type lock;
{
// Accessing the root node requires defending against concurrent pointer
// changes Thus we couple rootLock -> lock on root node -> validate rootLock
@@ -878,7 +879,7 @@ restart:
goto restart;
if (!iter)
return NULL;
- uintptr_t child_lock;
+ uintptr_type child_lock;
if ((!btree_node_lock_optimistic (iter, &child_lock))
|| (!version_lock_validate (&(t->root_lock), lock)))
goto restart;
@@ -910,7 +911,7 @@ restart:
// The node content can change at any point in time, thus we must
// interleave parent and child checks.
- uintptr_t child_lock;
+ uintptr_type child_lock;
if (!btree_node_lock_optimistic (child, &child_lock))
goto restart;
if (!btree_node_validate (iter, lock))
diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
index a591faaa579b5883..f38efd3c09efc3e9 100644
--- a/libgcc/unwind-dw2-fde.c
+++ b/libgcc/unwind-dw2-fde.c
@@ -42,6 +42,8 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#endif
#endif
+typedef __UINTPTR_TYPE__ uintptr_type;
+
#ifdef ATOMIC_FDE_FAST_PATH
#include "unwind-dw2-btree.h"
@@ -58,7 +60,7 @@ release_registered_frames (void)
}
static void
-get_pc_range (const struct object *ob, uintptr_t *range);
+get_pc_range (const struct object *ob, uintptr_type *range);
static void
init_object (struct object *ob);
@@ -124,7 +126,7 @@ __register_frame_info_bases (const void *begin, struct object *ob,
init_object (ob);
// And register the frame
- uintptr_t range[2];
+ uintptr_type range[2];
get_pc_range (ob, range);
btree_insert (&registered_frames, range[0], range[1] - range[0], ob);
#else
@@ -178,7 +180,7 @@ __register_frame_info_table_bases (void *begin, struct object *ob,
init_object (ob);
// And register the frame
- uintptr_t range[2];
+ uintptr_type range[2];
get_pc_range (ob, range);
btree_insert (&registered_frames, range[0], range[1] - range[0], ob);
#else
@@ -237,7 +239,7 @@ __deregister_frame_info_bases (const void *begin)
#ifdef DWARF2_OBJECT_END_PTR_EXTENSION
lookupob.fde_end = NULL;
#endif
- uintptr_t range[2];
+ uintptr_type range[2];
get_pc_range (&lookupob, range);
// And remove
@@ -677,7 +679,7 @@ end_fde_sort (struct object *ob, struct fde_accumulator *accu, size_t count)
static size_t
classify_object_over_fdes (struct object *ob, const fde *this_fde,
- uintptr_t *range)
+ uintptr_type *range)
{
const struct dwarf_cie *last_cie = 0;
size_t count = 0;
@@ -892,11 +894,11 @@ init_object (struct object* ob)
#ifdef ATOMIC_FDE_FAST_PATH
/* Get the PC range for lookup */
static void
-get_pc_range (const struct object *ob, uintptr_t *range)
+get_pc_range (const struct object *ob, uintptr_type *range)
{
// It is safe to cast to non-const object* here as
// classify_object_over_fdes does not modify ob in query mode.
- struct object *ncob = (struct object *) (uintptr_t) ob;
+ struct object *ncob = (struct object *) (uintptr_type) ob;
range[0] = range[1] = 0;
if (ob->s.b.sorted)
{
@@ -1131,7 +1133,7 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
const fde *f = NULL;
#ifdef ATOMIC_FDE_FAST_PATH
- ob = btree_lookup (&registered_frames, (uintptr_t) pc);
+ ob = btree_lookup (&registered_frames, (uintptr_type) pc);
if (!ob)
return NULL;

View File

@ -0,0 +1,27 @@
commit 190c644c06369766aa2537851ddbf83b1231b65b
Author: Philipp Fent <fent@in.tum.de>
Date: Sun Sep 4 20:47:34 2022 +0200
libstdc++: Fix pretty printer tests of tuple indexes
Signed-off-by: Philipp Fent <fent@in.tum.de>
libstdc++-v3/ChangeLog:
* testsuite/libstdc++-prettyprinters/48362.cc: Fix expected
tuple indices.
* testsuite/libstdc++-prettyprinters/cxx11.cc: Likewise.
diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc
index cc91803e247..af335d0d3c7 100644
--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc
+++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc
@@ -29,7 +29,7 @@ main()
// { dg-final { note-test t1 {empty std::tuple} } }
std::tuple<std::string, int, std::tuple<>> t2{ "Johnny", 5, {} };
-// { dg-final { regexp-test t2 {std::tuple containing = {\[1\] = "Johnny", \[2\] = 5, \[3\] = empty std::tuple}} } }
+// { dg-final { regexp-test t2 {std::tuple containing = {\[0\] = "Johnny", \[1\] = 5, \[2\] = empty std::tuple}} } }
std::cout << "\n";
return 0; // Mark SPOT

View File

@ -0,0 +1,270 @@
.../testsuite/libstdc++-prettyprinters/compat.cc | 10 +++----
.../testsuite/libstdc++-prettyprinters/cxx11.cc | 33 +++++++++++++++++-----
.../testsuite/libstdc++-prettyprinters/cxx17.cc | 27 ++++++++----------
.../libstdc++-prettyprinters/filesystem-ts.cc | 2 +-
.../libstdc++-prettyprinters/libfundts.cc | 25 ++++++++--------
5 files changed, 58 insertions(+), 39 deletions(-)
diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/compat.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/compat.cc
index 35243e5f892..2ef5979834f 100644
--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/compat.cc
+++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/compat.cc
@@ -1,7 +1,7 @@
// { dg-options "-g -O0" }
// { dg-do run { target c++11 } }
-// Copyright (C) 2014-2021 Free Software Foundation, Inc.
+// Copyright (C) 2014-2025 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
@@ -102,13 +102,13 @@ main()
using std::optional;
optional<int> o;
-// { dg-final { note-test o {std::optional<int> [no contained value]} } }
+// { dg-final { note-test o {std::optional [no contained value]} } }
optional<bool> ob{false};
-// { dg-final { note-test ob {std::optional<bool> = {[contained value] = false}} } }
+// { dg-final { note-test ob {std::optional = {[contained value] = false}} } }
optional<int> oi{5};
-// { dg-final { note-test oi {std::optional<int> = {[contained value] = 5}} } }
+// { dg-final { note-test oi {std::optional = {[contained value] = 5}} } }
optional<void*> op{nullptr};
-// { dg-final { note-test op {std::optional<void *> = {[contained value] = 0x0}} } }
+// { dg-final { note-test op {std::optional = {[contained value] = 0x0}} } }
__builtin_puts("");
return 0; // Mark SPOT
diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc
index 0545076fb6f..23f6d97ddd4 100644
--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc
+++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc
@@ -1,7 +1,7 @@
// { dg-do run { target c++11 } }
// { dg-options "-g -O0" }
-// Copyright (C) 2011-2021 Free Software Foundation, Inc.
+// Copyright (C) 2011-2025 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
@@ -26,6 +26,7 @@
#include <iostream>
#include <future>
#include <initializer_list>
+#include <atomic>
#include "../util/testsuite_allocator.h" // NullablePointer
typedef std::tuple<int, int> ExTuple;
@@ -62,6 +63,11 @@ struct datum
std::unique_ptr<datum> global;
+struct custom_cat : std::error_category {
+ const char* name() const noexcept { return "miaow"; }
+ std::string message(int) const { return ""; }
+};
+
int
main()
{
@@ -165,9 +171,9 @@ main()
// { dg-final { note-test runiq_ptr {std::unique_ptr<int> = {get() = 0x0}} } }
ExTuple tpl(6,7);
-// { dg-final { note-test tpl {std::tuple containing = {[1] = 6, [2] = 7}} } }
+// { dg-final { note-test tpl {std::tuple containing = {[0] = 6, [1] = 7}} } }
ExTuple &rtpl = tpl;
-// { dg-final { note-test rtpl {std::tuple containing = {[1] = 6, [2] = 7}} } }
+// { dg-final { note-test rtpl {std::tuple containing = {[0] = 6, [1] = 7}} } }
std::error_code e0;
// { dg-final { note-test e0 {std::error_code = { }} } }
@@ -178,10 +184,7 @@ main()
std::error_condition ecinval = std::make_error_condition(std::errc::invalid_argument);
// { dg-final { note-test ecinval {std::error_condition = {"generic": EINVAL}} } }
- struct custom_cat : std::error_category {
- const char* name() const noexcept { return "miaow"; }
- std::string message(int) const { return ""; }
- } cat;
+ custom_cat cat;
std::error_code emiaow(42, cat);
// { dg-final { note-test emiaow {std::error_code = {custom_cat: 42}} } }
std::error_condition ecmiaow(42, cat);
@@ -197,6 +200,22 @@ main()
std::initializer_list<int> il = {3, 4};
// { dg-final { note-test il {std::initializer_list of length 2 = {3, 4}} } }
+ std::atomic<int> ai{100};
+ // { dg-final { note-test ai {std::atomic<int> = { 100 }} } }
+ long l{};
+ std::atomic<long*> ap{&l};
+ // { dg-final { regexp-test ap {std::atomic.long \*. = { 0x.* }} } }
+ struct Value { int i, j; };
+ std::atomic<Value> av{{8, 9}};
+ // { dg-final { note-test av {std::atomic<Value> = { {i = 8, j = 9} }} } }
+
+ std::integral_constant<int, 1> one;
+ // { dg-final { note-test one {std::integral_constant<int, 1>} } }
+ std::integral_constant<bool, true> truth;
+ // { dg-final { note-test truth {std::true_type} } }
+ std::integral_constant<bool, 0> lies;
+ // { dg-final { note-test lies {std::false_type} } }
+
placeholder(""); // Mark SPOT
use(efl);
use(fl);
diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc
index 72c66d3b785..6dd2b60c0a5 100644
--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc
+++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc
@@ -1,7 +1,7 @@
// { dg-options "-g -O0" }
// { dg-do run { target c++17 } }
-// Copyright (C) 2014-2021 Free Software Foundation, Inc.
+// Copyright (C) 2014-2025 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
@@ -18,9 +18,6 @@
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
-// Type printers only recognize the old std::string for now.
-#define _GLIBCXX_USE_CXX11_ABI 0
-
#include <filesystem>
#include <any>
#include <optional>
@@ -53,18 +50,18 @@ main()
// { dg-final { note-test str "\"string\"" } }
optional<int> o;
-// { dg-final { note-test o {std::optional<int> [no contained value]} } }
+// { dg-final { note-test o {std::optional [no contained value]} } }
optional<bool> ob{false};
-// { dg-final { note-test ob {std::optional<bool> = {[contained value] = false}} } }
+// { dg-final { note-test ob {std::optional = {[contained value] = false}} } }
optional<int> oi{5};
-// { dg-final { note-test oi {std::optional<int> = {[contained value] = 5}} } }
+// { dg-final { note-test oi {std::optional = {[contained value] = 5}} } }
optional<void*> op{nullptr};
-// { dg-final { note-test op {std::optional<void *> = {[contained value] = 0x0}} } }
+// { dg-final { note-test op {std::optional = {[contained value] = 0x0}} } }
optional<std::map<int, double>> om;
om = std::map<int, double>{ {1, 2.}, {3, 4.}, {5, 6.} };
-// { dg-final { regexp-test om {std::optional<std::(__debug::)?map<int, double>> containing std::(__debug::)?map with 3 elements = {\[1\] = 2, \[3\] = 4, \[5\] = 6}} } }
+// { dg-final { regexp-test om {std::optional containing std::(__debug::)?map with 3 elements = {\[1\] = 2, \[3\] = 4, \[5\] = 6}} } }
optional<std::string> os{ "stringy" };
-// { dg-final { note-test os {std::optional<std::string> = {[contained value] = "stringy"}} } }
+// { dg-final { note-test os {std::optional = {[contained value] = "stringy"}} } }
any a;
// { dg-final { note-test a {std::any [no contained value]} } }
@@ -86,18 +83,18 @@ main()
struct S { operator int() { throw 42; }};
variant<float, int, string_view> v0;
-// { dg-final { note-test v0 {std::variant<float, int, std::string_view> [index 0] = {0}} } }
+// { dg-final { note-test v0 {std::variant [index 0] = {0}} } }
variant<float, int, string_view> v1{ 0.5f };
-// { dg-final { note-test v1 {std::variant<float, int, std::string_view> [index 0] = {0.5}} } }
+// { dg-final { note-test v1 {std::variant [index 0] = {0.5}} } }
variant<float, X, string_view> v2;
try {
v2.emplace<1>(S());
} catch (int) { }
-// { dg-final { note-test v2 {std::variant<float, X, std::string_view> [no contained value]} } }
+// { dg-final { note-test v2 {std::variant [no contained value]} } }
variant<float, int, string_view> v3{ 3 };
-// { dg-final { note-test v3 {std::variant<float, int, std::string_view> [index 1] = {3}} } }
+// { dg-final { note-test v3 {std::variant [index 1] = {3}} } }
variant<float, int, string_view> v4{ str };
-// { dg-final { note-test v4 {std::variant<float, int, std::string_view> [index 2] = {"string"}} } }
+// { dg-final { note-test v4 {std::variant [index 2] = {"string"}} } }
map<int, string_view> m{ {1, "one"} };
map<int, string_view>::node_type n0;
diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/filesystem-ts.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/filesystem-ts.cc
index 00d100bd066..3221f2df90d 100644
--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/filesystem-ts.cc
+++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/filesystem-ts.cc
@@ -2,7 +2,7 @@
// { dg-do run { target c++11 } }
// { dg-require-filesystem-ts "" }
-// Copyright (C) 2020-2021 Free Software Foundation, Inc.
+// Copyright (C) 2020-2025 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/libfundts.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/libfundts.cc
index 85005c0617f..bfb86885457 100644
--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/libfundts.cc
+++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/libfundts.cc
@@ -1,7 +1,7 @@
// { dg-do run { target c++14 } }
// { dg-options "-g -O0" }
-// Copyright (C) 2014-2021 Free Software Foundation, Inc.
+// Copyright (C) 2014-2025 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
@@ -18,9 +18,6 @@
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
-// Type printers only recognize the old std::string for now.
-#define _GLIBCXX_USE_CXX11_ABI 0
-
#include <experimental/any>
#include <experimental/optional>
#include <experimental/string_view>
@@ -35,22 +32,28 @@ using std::experimental::string_view;
int
main()
{
+ // Ensure debug info for std::string is issued in the local
+ // translation unit, so that GDB won't pick up any alternate
+ // std::string notion that might be present in libstdc++.so.
+ std::string bah = "hi";
+ (void)bah;
+
string_view str = "string";
// { dg-final { note-test str "\"string\"" } }
optional<int> o;
-// { dg-final { note-test o {std::experimental::optional<int> [no contained value]} } }
+// { dg-final { note-test o {std::experimental::optional [no contained value]} } }
optional<bool> ob{false};
-// { dg-final { note-test ob {std::experimental::optional<bool> = {[contained value] = false}} } }
+// { dg-final { note-test ob {std::experimental::optional = {[contained value] = false}} } }
optional<int> oi{5};
-// { dg-final { note-test oi {std::experimental::optional<int> = {[contained value] = 5}} } }
+// { dg-final { note-test oi {std::experimental::optional = {[contained value] = 5}} } }
optional<void*> op{nullptr};
-// { dg-final { note-test op {std::experimental::optional<void *> = {[contained value] = 0x0}} } }
+// { dg-final { note-test op {std::experimental::optional = {[contained value] = 0x0}} } }
optional<std::map<int, double>> om;
om = std::map<int, double>{ {1, 2.}, {3, 4.}, {5, 6.} };
-// { dg-final { regexp-test om {std::experimental::optional<std::(__debug::)?map<int, double>> containing std::(__debug::)?map with 3 elements = {\[1\] = 2, \[3\] = 4, \[5\] = 6}} } }
+// { dg-final { regexp-test om {std::experimental::optional containing std::(__debug::)?map with 3 elements = {\[1\] = 2, \[3\] = 4, \[5\] = 6}} } }
optional<std::string> os{ "stringy" };
-// { dg-final { note-test os {std::experimental::optional<std::string> = {[contained value] = "stringy"}} { xfail { c++20 || debug_mode } } } }
+// { dg-final { note-test os {std::experimental::optional = {[contained value] = "stringy"}} } }
any a;
// { dg-final { note-test a {std::experimental::any [no contained value]} } }
@@ -61,7 +64,7 @@ main()
any ap = (void*)nullptr;
// { dg-final { note-test ap {std::experimental::any containing void * = {[contained value] = 0x0}} } }
any as = *os;
-// { dg-final { note-test as {std::experimental::any containing std::string = {[contained value] = "stringy"}} { xfail { c++20 || debug_mode } } } }
+// { dg-final { note-test as {std::experimental::any containing std::string = {[contained value] = "stringy"}} } }
any as2("stringiest");
// { dg-final { regexp-test as2 {std::experimental::any containing const char \* = {\[contained value\] = 0x[[:xdigit:]]+ "stringiest"}} } }
any am = *om;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
Original patch (taken from GCC 12 branch version) edited for GCC 11 to
use .c rather than .cc filenames.
commit 587b370c8492aadaab14c57e242c66778cc78891
Author: Richard Sandiford <richard.sandiford@arm.com>
Date: Tue Mar 11 15:51:55 2025 +0000
Fix folding of BIT_NOT_EXPR for POLY_INT_CST [PR118976]
There was an embarrassing typo in the folding of BIT_NOT_EXPR for
POLY_INT_CSTs: it used - rather than ~ on the poly_int. Not sure
how that happened, but it might have been due to the way that
~x is implemented as -1 - x internally.
gcc/
PR tree-optimization/118976
* fold-const.cc (const_unop): Use ~ rather than - for BIT_NOT_EXPR.
* config/aarch64/aarch64.cc (aarch64_test_sve_folding): New function.
(aarch64_run_selftests): Run it.
(cherry picked from commit 78380fd7f743e23dfdf013d68a2f0347e1511550)
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index be0d958dcf6b..72d737d62228 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -27541,6 +27541,16 @@ aarch64_test_fractional_cost ()
ASSERT_EQ (cf (1, 2).as_double (), 0.5);
}
+/* Test SVE arithmetic folding. */
+
+static void
+aarch64_test_sve_folding ()
+{
+ tree res = fold_unary (BIT_NOT_EXPR, ssizetype,
+ ssize_int (poly_int64 (1, 1)));
+ ASSERT_TRUE (operand_equal_p (res, ssize_int (poly_int64 (-2, -1))));
+}
+
/* Run all target-specific selftests. */
static void
@@ -27548,6 +27558,7 @@ aarch64_run_selftests (void)
{
aarch64_test_loading_full_dump ();
aarch64_test_fractional_cost ();
+ aarch64_test_sve_folding ();
}
} // namespace selftest
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index d81a71c41a17..391f11095408 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -1802,7 +1802,7 @@ const_unop (enum tree_code code, tree type, tree arg0)
if (TREE_CODE (arg0) == INTEGER_CST)
return fold_not_const (arg0, type);
else if (POLY_INT_CST_P (arg0))
- return wide_int_to_tree (type, -poly_int_cst_value (arg0));
+ return wide_int_to_tree (type, ~poly_int_cst_value (arg0));
/* Perform BIT_NOT_EXPR on each element individually. */
else if (TREE_CODE (arg0) == VECTOR_CST)
{

403
SOURCES/gcc11-pr99888.patch Normal file
View File

@ -0,0 +1,403 @@
From c23b5006d3ffeda1a9edf5fd817765a6da3696ca Mon Sep 17 00:00:00 2001
From: Kewen Lin <linkw@linux.ibm.com>
Date: Fri, 30 Sep 2022 07:16:49 -0500
Subject: [PATCH] rs6000: Rework ELFv2 support for -fpatchable-function-entry*
[PR99888]
As PR99888 and its related show, the current support for
-fpatchable-function-entry on powerpc ELFv2 doesn't work
well with global entry existence. For example, with one
command line option -fpatchable-function-entry=3,2, it got
below w/o this patch:
.LPFE1:
nop
nop
.type foo, @function
foo:
nop
.LFB0:
.cfi_startproc
.LCF0:
0: addis 2,12,.TOC.-.LCF0@ha
addi 2,2,.TOC.-.LCF0@l
.localentry foo,.-foo
, the assembly is unexpected since the patched nops have
no effects when being entered from local entry.
This patch is to update the nops patched before and after
local entry, it looks like:
.type foo, @function
foo:
.LFB0:
.cfi_startproc
.LCF0:
0: addis 2,12,.TOC.-.LCF0@ha
addi 2,2,.TOC.-.LCF0@l
nop
nop
.localentry foo,.-foo
nop
PR target/99888
PR target/105649
Backported to GCC 11: renamed source files from .cc back to .c
gcc/ChangeLog:
* doc/invoke.texi (option -fpatchable-function-entry): Adjust the
documentation for PowerPC ELFv2 ABI dual entry points.
* config/rs6000/rs6000-internal.h
(rs6000_print_patchable_function_entry): New function declaration.
* config/rs6000/rs6000-logue.c (rs6000_output_function_prologue):
Support patchable-function-entry by emitting nops before and after
local entry for the function that needs global entry.
* config/rs6000/rs6000.c (rs6000_print_patchable_function_entry): Skip
the function that needs global entry till global entry has been
emitted.
* config/rs6000/rs6000.h (struct machine_function): New bool member
global_entry_emitted.
gcc/testsuite/ChangeLog:
* gcc.target/powerpc/pr99888-1.c: New test.
* gcc.target/powerpc/pr99888-2.c: New test.
* gcc.target/powerpc/pr99888-3.c: New test.
* gcc.target/powerpc/pr99888-4.c: New test.
* gcc.target/powerpc/pr99888-5.c: New test.
* gcc.target/powerpc/pr99888-6.c: New test.
* c-c++-common/patchable_function_entry-default.c: Adjust for
powerpc_elfv2 to avoid compilation error.
---
gcc/config/rs6000/rs6000-internal.h | 4 ++
gcc/config/rs6000/rs6000-logue.c | 32 ++++++++++++++
gcc/config/rs6000/rs6000.c | 10 ++++-
gcc/config/rs6000/rs6000.h | 4 ++
gcc/doc/invoke.texi | 8 +++-
.../patchable_function_entry-default.c | 3 ++
gcc/testsuite/gcc.target/powerpc/pr99888-1.c | 43 +++++++++++++++++++
gcc/testsuite/gcc.target/powerpc/pr99888-2.c | 43 +++++++++++++++++++
gcc/testsuite/gcc.target/powerpc/pr99888-3.c | 11 +++++
gcc/testsuite/gcc.target/powerpc/pr99888-4.c | 13 ++++++
gcc/testsuite/gcc.target/powerpc/pr99888-5.c | 13 ++++++
gcc/testsuite/gcc.target/powerpc/pr99888-6.c | 14 ++++++
12 files changed, 194 insertions(+), 4 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/powerpc/pr99888-1.c
create mode 100644 gcc/testsuite/gcc.target/powerpc/pr99888-2.c
create mode 100644 gcc/testsuite/gcc.target/powerpc/pr99888-3.c
create mode 100644 gcc/testsuite/gcc.target/powerpc/pr99888-4.c
create mode 100644 gcc/testsuite/gcc.target/powerpc/pr99888-5.c
create mode 100644 gcc/testsuite/gcc.target/powerpc/pr99888-6.c
diff --git a/gcc/config/rs6000/rs6000-internal.h b/gcc/config/rs6000/rs6000-internal.h
index b9e82c0468d0..e75b8d5c7e88 100644
--- a/gcc/config/rs6000/rs6000-internal.h
+++ b/gcc/config/rs6000/rs6000-internal.h
@@ -182,6 +182,10 @@ extern tree rs6000_fold_builtin (tree fndecl ATTRIBUTE_UNUSED,
tree *args ATTRIBUTE_UNUSED,
bool ignore ATTRIBUTE_UNUSED);
+extern void rs6000_print_patchable_function_entry (FILE *,
+ unsigned HOST_WIDE_INT,
+ bool);
+
extern bool rs6000_passes_float;
extern bool rs6000_passes_long_double;
extern bool rs6000_passes_vector;
diff --git a/gcc/config/rs6000/rs6000-logue.c b/gcc/config/rs6000/rs6000-logue.c
index a11d020ccd0c..3621cb501c70 100644
--- a/gcc/config/rs6000/rs6000-logue.c
+++ b/gcc/config/rs6000/rs6000-logue.c
@@ -4009,11 +4009,43 @@ rs6000_output_function_prologue (FILE *file)
fprintf (file, "\tadd 2,2,12\n");
}
+ unsigned short patch_area_size = crtl->patch_area_size;
+ unsigned short patch_area_entry = crtl->patch_area_entry;
+ /* Need to emit the patching area. */
+ if (patch_area_size > 0)
+ {
+ cfun->machine->global_entry_emitted = true;
+ /* As ELFv2 ABI shows, the allowable bytes between the global
+ and local entry points are 0, 4, 8, 16, 32 and 64 when
+ there is a local entry point. Considering there are two
+ non-prefixed instructions for global entry point prologue
+ (8 bytes), the count for patchable nops before local entry
+ point would be 2, 6 and 14. It's possible to support those
+ other counts of nops by not making a local entry point, but
+ we don't have clear use cases for them, so leave them
+ unsupported for now. */
+ if (patch_area_entry > 0)
+ {
+ if (patch_area_entry != 2
+ && patch_area_entry != 6
+ && patch_area_entry != 14)
+ error ("unsupported number of nops before function entry (%u)",
+ patch_area_entry);
+ rs6000_print_patchable_function_entry (file, patch_area_entry,
+ true);
+ patch_area_size -= patch_area_entry;
+ }
+ }
+
fputs ("\t.localentry\t", file);
assemble_name (file, name);
fputs (",.-", file);
assemble_name (file, name);
fputs ("\n", file);
+ /* Emit the nops after local entry. */
+ if (patch_area_size > 0)
+ rs6000_print_patchable_function_entry (file, patch_area_size,
+ patch_area_entry == 0);
}
else if (rs6000_pcrel_p ())
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index bbe21eacc6b9..b9496d7f2680 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -14930,8 +14930,14 @@ rs6000_print_patchable_function_entry (FILE *file,
if (!(TARGET_64BIT && DEFAULT_ABI != ABI_ELFv2)
&& HAVE_GAS_SECTION_LINK_ORDER)
flags |= SECTION_LINK_ORDER;
- default_print_patchable_function_entry_1 (file, patch_area_size, record_p,
- flags);
+ bool global_entry_needed_p = rs6000_global_entry_point_prologue_needed_p ();
+ /* For a function which needs global entry point, we will emit the
+ patchable area before and after local entry point under the control of
+ cfun->machine->global_entry_emitted, see the handling in function
+ rs6000_output_function_prologue. */
+ if (!global_entry_needed_p || cfun->machine->global_entry_emitted)
+ default_print_patchable_function_entry_1 (file, patch_area_size, record_p,
+ flags);
}
enum rtx_code
diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h
index eb7b21584970..b4df22b60303 100644
--- a/gcc/config/rs6000/rs6000.h
+++ b/gcc/config/rs6000/rs6000.h
@@ -2435,6 +2435,10 @@ typedef struct GTY(()) machine_function
bool lr_is_wrapped_separately;
bool toc_is_wrapped_separately;
bool mma_return_type_error;
+ /* Indicate global entry is emitted, only useful when the function requires
+ global entry. It helps to control the patchable area before and after
+ local entry. */
+ bool global_entry_emitted;
} machine_function;
#endif
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 2ac9cfc35f92..518bfdf0867d 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -16884,9 +16884,13 @@ the area size or to remove it completely on a single function.
If @code{N=0}, no pad location is recorded.
The NOP instructions are inserted at---and maybe before, depending on
-@var{M}---the function entry address, even before the prologue.
+@var{M}---the function entry address, even before the prologue. On
+PowerPC with the ELFv2 ABI, for a function with dual entry points,
+the local entry point is this function entry address.
-The maximum value of @var{N} and @var{M} is 65535.
+The maximum value of @var{N} and @var{M} is 65535. On PowerPC with the
+ELFv2 ABI, for a function with dual entry points, the supported values
+for @var{M} are 0, 2, 6 and 14.
@end table
diff --git a/gcc/testsuite/c-c++-common/patchable_function_entry-default.c b/gcc/testsuite/c-c++-common/patchable_function_entry-default.c
index 7036f7bfbea4..a501efccb194 100644
--- a/gcc/testsuite/c-c++-common/patchable_function_entry-default.c
+++ b/gcc/testsuite/c-c++-common/patchable_function_entry-default.c
@@ -1,6 +1,9 @@
/* { dg-do compile { target { ! { nvptx*-*-* visium-*-* } } } } */
/* { dg-options "-O2 -fpatchable-function-entry=3,1" } */
/* { dg-additional-options "-fno-pie" { target sparc*-*-* } } */
+/* See PR99888, one single preceding nop isn't allowed on powerpc_elfv2,
+ so overriding with two preceding nops to make it pass there. */
+/* { dg-additional-options "-fpatchable-function-entry=3,2" { target powerpc_elfv2 } } */
/* { dg-final { scan-assembler-times "nop|NOP|SWYM" 3 { target { ! { alpha*-*-* } } } } } */
/* { dg-final { scan-assembler-times "bis" 3 { target alpha*-*-* } } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/pr99888-1.c b/gcc/testsuite/gcc.target/powerpc/pr99888-1.c
new file mode 100644
index 000000000000..9370b4e74388
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr99888-1.c
@@ -0,0 +1,43 @@
+/* Verify no errors for different nops after local entry on ELFv2. */
+
+extern int a;
+
+__attribute__ ((noipa, patchable_function_entry (1, 0)))
+int test1 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (2, 0)))
+int test2 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (3, 0)))
+int test3 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (4, 0)))
+int test4 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (5, 0)))
+int test5 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (6, 0)))
+int test6 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (7, 0)))
+int test7 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (8, 0)))
+int test8 (int b) {
+ return a + b;
+}
diff --git a/gcc/testsuite/gcc.target/powerpc/pr99888-2.c b/gcc/testsuite/gcc.target/powerpc/pr99888-2.c
new file mode 100644
index 000000000000..450617126023
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr99888-2.c
@@ -0,0 +1,43 @@
+/* Verify no errors for 2, 6 and 14 nops before local entry on ELFv2. */
+
+extern int a;
+
+__attribute__ ((noipa, patchable_function_entry (2, 2)))
+int test1 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (4, 2)))
+int test2 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (6, 6)))
+int test3 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (8, 6)))
+int test4 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (16, 6)))
+int test5 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (14, 14)))
+int test6 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (28, 14)))
+int test7 (int b) {
+ return a + b;
+}
+
+__attribute__ ((noipa, patchable_function_entry (64, 14)))
+int test8 (int b) {
+ return a + b;
+}
diff --git a/gcc/testsuite/gcc.target/powerpc/pr99888-3.c b/gcc/testsuite/gcc.target/powerpc/pr99888-3.c
new file mode 100644
index 000000000000..4531ae32036d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr99888-3.c
@@ -0,0 +1,11 @@
+/* { dg-options "-fpatchable-function-entry=1" } */
+
+/* Verify no errors on ELFv2, using command line option instead of
+ function attribute. */
+
+extern int a;
+
+int test (int b) {
+ return a + b;
+}
+
diff --git a/gcc/testsuite/gcc.target/powerpc/pr99888-4.c b/gcc/testsuite/gcc.target/powerpc/pr99888-4.c
new file mode 100644
index 000000000000..00a8d4d316e0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr99888-4.c
@@ -0,0 +1,13 @@
+/* { dg-require-effective-target powerpc_elfv2 } */
+/* There is no global entry point prologue with pcrel. */
+/* { dg-options "-mno-pcrel -fpatchable-function-entry=1,1" } */
+
+/* Verify one error emitted for unexpected 1 nop before local
+ entry. */
+
+extern int a;
+
+int test (int b) {
+ return a + b;
+}
+/* { dg-error "unsupported number of nops before function entry \\(1\\)" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/gcc.target/powerpc/pr99888-5.c b/gcc/testsuite/gcc.target/powerpc/pr99888-5.c
new file mode 100644
index 000000000000..39d3b4465f11
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr99888-5.c
@@ -0,0 +1,13 @@
+/* { dg-require-effective-target powerpc_elfv2 } */
+/* There is no global entry point prologue with pcrel. */
+/* { dg-options "-mno-pcrel -fpatchable-function-entry=7,3" } */
+
+/* Verify one error emitted for unexpected 3 nops before local
+ entry. */
+
+extern int a;
+
+int test (int b) {
+ return a + b;
+}
+/* { dg-error "unsupported number of nops before function entry \\(3\\)" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/gcc.target/powerpc/pr99888-6.c b/gcc/testsuite/gcc.target/powerpc/pr99888-6.c
new file mode 100644
index 000000000000..c6c18dcc7ac0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr99888-6.c
@@ -0,0 +1,14 @@
+/* { dg-require-effective-target powerpc_elfv2 } */
+/* There is no global entry point prologue with pcrel. */
+/* { dg-options "-mno-pcrel" } */
+
+/* Verify one error emitted for unexpected 4 nops before local
+ entry. */
+
+extern int a;
+
+__attribute__ ((patchable_function_entry (20, 4)))
+int test (int b) {
+ return a + b;
+}
+/* { dg-error "unsupported number of nops before function entry \\(4\\)" "" { target *-*-* } .-1 } */
--
2.43.5

View File

@ -0,0 +1,38 @@
commit 181f40f5cf8510a16191e4768dadbe2cb7a5c095
Author: Jakub Jelinek <jakub@redhat.com>
Date: Wed Jul 24 18:00:05 2024 +0200
testsuite: Fix up pr116034.c test for big/pdp endian [PR116061]
Didn't notice the memmove is into an int variable, so the test
was still failing on big endian.
2024-07-24 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/116034
PR testsuite/116061
* gcc.dg/pr116034.c (g): Change type from int to unsigned short.
(foo): Guard memmove call on __SIZEOF_SHORT__ == 2.
(cherry picked from commit 69e69847e21a8d951ab5f09fd3421449564dba31)
diff --git a/gcc/testsuite/gcc.dg/pr116034.c b/gcc/testsuite/gcc.dg/pr116034.c
index 9a31de03424..955b4c9e86b 100644
--- a/gcc/testsuite/gcc.dg/pr116034.c
+++ b/gcc/testsuite/gcc.dg/pr116034.c
@@ -2,12 +2,13 @@
/* { dg-do run } */
/* { dg-options "-O1 -fno-strict-aliasing" } */
-int g;
+unsigned short int g;
static inline int
foo (_Complex unsigned short c)
{
- __builtin_memmove (&g, 1 + (char *) &c, 2);
+ if (__SIZEOF_SHORT__ == 2)
+ __builtin_memmove (&g, 1 + (char *) &c, 2);
return g;
}

View File

@ -1,10 +1,11 @@
%global source_date_epoch_from_changelog 1
%global DATE 20240719
%global gitrev a985e3068a6f8045f8a6f2d2d5ae75f5eb0a8767
%global gcc_version 11.5.0
%global gcc_major 11
# Note, gcc_release must be integer, if you want to add suffixes to
# %%{release}, append them after %%{gcc_release} on Release: line.
%global gcc_release 2
%global gcc_release 11
%global nvptx_tools_gitrev 5f6f343a302d620b0868edab376c00b15741e39e
%global newlib_cygwin_gitrev 50e2a63b04bdd018484605fbb954fd1bd5147fa0
%global _unpackaged_files_terminate_build 0
@ -150,7 +151,7 @@ Source0: gcc-%{version}-%{DATE}.tar.xz
Source1: nvptx-tools-%{nvptx_tools_gitrev}.tar.xz
# The source for nvptx-newlib package was pulled from upstream's vcs. Use the
# following commands to generate the tarball:
# git clone git://sourceware.org/git/newlib-cygwin.git newlib-cygwin-dir.tmp
# git clone https://sourceware.org/git/newlib-cygwin.git newlib-cygwin-dir.tmp
# git --git-dir=newlib-cygwin-dir.tmp/.git archive --prefix=newlib-cygwin-%%{newlib_cygwin_gitrev}/ %%{newlib_cygwin_gitrev} ":(exclude)newlib/libc/sys/linux/include/rpc/*.[hx]" | xz -9e > newlib-cygwin-%%{newlib_cygwin_gitrev}.tar.xz
# rm -rf newlib-cygwin-dir.tmp
Source2: newlib-cygwin-%{newlib_cygwin_gitrev}.tar.xz
@ -202,7 +203,10 @@ BuildRequires: glibc >= 2.3.90-35
%endif
%ifarch %{multilib_64_archs} sparcv9 ppc
# Ensure glibc{,-devel} is installed for both multilib arches
BuildRequires: /lib/libc.so.6 /usr/lib/libc.so /lib64/libc.so.6 /usr/lib64/libc.so
BuildRequires: (glibc32 or glibc-devel(%{__isa_name}-32))
%endif
%ifarch sparcv9 ppc
BuildRequires: (glibc64 or glibc-devel(%{__isa_name}-64))
%endif
%if %{build_ada}
# Ada requires Ada to build
@ -298,6 +302,36 @@ Patch35: gcc11-testsuite-aarch64-add-fno-stack-protector.patch
Patch36: gcc11-libgfortran-flush.patch
Patch37: gcc11-pr113960.patch
Patch38: gcc11-pr105157.patch
Patch39: gcc11-testsuite-fixes-4.patch
Patch40: gcc11-pr99888.patch
Patch41: gcc11-pr118976.patch
Patch42: gcc-RHEL-105072-1.patch
Patch43: gcc-RHEL-105072-2.patch
Patch44: gcc-RHEL-105072-3.patch
Patch45: gcc-RHEL-105072-4.patch
Patch46: gcc-RHEL-105072-5.patch
Patch47: gcc-RHEL-105072-6.patch
Patch48: gcc-RHEL-105072-7.patch
Patch49: gcc-RHEL-105072-8.patch
Patch50: gcc-RHEL-105072-9.patch
Patch51: gcc-RHEL-105072-10.patch
Patch52: gcc-RHEL-105072-11.patch
Patch53: gcc-RHEL-105072-12.patch
Patch54: gcc-RHEL-105072-13.patch
Patch55: gcc-RHEL-105072-14.patch
Patch56: gcc-RHEL-105072-15.patch
Patch57: gcc-RHEL-105072-16.patch
Patch58: gcc-RHEL-105072-17.patch
Patch59: gcc-RHEL-105072-18.patch
Patch60: gcc-RHEL-105072-19.patch
Patch61: gcc-RHEL-105072-20.patch
Patch62: gcc-RHEL-105072-21.patch
Patch63: gcc-RHEL-105072-22.patch
Patch64: gcc-RHEL-105072-23.patch
Patch65: gcc-RHEL-105072-24.patch
Patch66: gcc-RHEL-105072-25.patch
Patch67: gcc-RHEL-105072-26.patch
Patch68: gcc-RHEL-105072-27.patch
Patch100: gcc11-fortran-fdec-duplicates.patch
Patch101: gcc11-fortran-flogical-as-integer.patch
@ -310,6 +344,11 @@ Patch107: gcc11-fortran-fdec-promotion.patch
Patch108: gcc11-fortran-fdec-sequence.patch
Patch109: gcc11-fortran-fdec-add-missing-indexes.patch
# Pretty printer update.
Patch1000: gcc11-libstdc++-prettyprinter-update-15.patch
Patch1001: gcc11-libstdc++-prettyprinter-update-15-tests.patch
Patch1002: gcc11-libstdc++-prettyprinter-update-15-tests-48362.patch
# On ARM EABI systems, we do want -gnueabi to be part of the
# target triple.
%ifnarch %{arm}
@ -355,6 +394,8 @@ You'll need this package in order to compile C code.
%package -n libgcc
Summary: GCC version 11 shared support library
Autoreq: false
# This expresses the dependency on _dl_find_object.
Requires: libc.so.6(GLIBC_2.35)%[0%{?__isa_bits} == 64 ? "(64bit)" : ""]
%if !%{build_ada}
Obsoletes: libgnat < %{version}-%{release}
%endif
@ -901,6 +942,36 @@ mark them as cross compiled.
%patch36 -p1 -b .libgfortran-flush~
%patch37 -p1 -b .pr113960~
%patch38 -p1 -b .pr105157~
%patch39 -p1 -b .testsuite4~
%patch40 -p1 -b .pr99888~
%patch41 -p1 -b .pr118976~
%patch42 -p1 -b .rhel-105072-1~
%patch43 -p1 -b .rhel-105072-2~
%patch44 -p1 -b .rhel-105072-3~
%patch45 -p1 -b .rhel-105072-4~
%patch46 -p1 -b .rhel-105072-5~
%patch47 -p1 -b .rhel-105072-6~
%patch48 -p1 -b .rhel-105072-7~
%patch49 -p1 -b .rhel-105072-8~
%patch50 -p1 -b .rhel-105072-9~
%patch51 -p1 -b .rhel-105072-10~
%patch52 -p1 -b .rhel-105072-11~
%patch53 -p1 -b .rhel-105072-12~
%patch54 -p1 -b .rhel-105072-13~
%patch55 -p1 -b .rhel-105072-14~
%patch56 -p1 -b .rhel-105072-15~
%patch57 -p1 -b .rhel-105072-16~
%patch58 -p1 -b .rhel-105072-17~
%patch59 -p1 -b .rhel-105072-18~
%patch60 -p1 -b .rhel-105072-19~
%patch61 -p1 -b .rhel-105072-20~
%patch62 -p1 -b .rhel-105072-21~
%patch63 -p1 -b .rhel-105072-22~
%patch64 -p1 -b .rhel-105072-23~
%patch65 -p1 -b .rhel-105072-24~
%patch66 -p1 -b .rhel-105072-25~
%patch67 -p1 -b .rhel-105072-26~
%patch68 -p1 -b .rhel-105072-27~
%if 0%{?rhel} >= 9
%patch100 -p1 -b .fortran-fdec-duplicates~
@ -915,6 +986,10 @@ mark them as cross compiled.
%patch109 -p1 -b .fortran-fdec-add-missing-indexes~
%endif
%patch1000 -p1 -b .libstdc++-prettyprinter-update-15
%patch1001 -p1 -b .libstdc++-prettyprinter-update-15-tests
%patch1002 -p1 -b .libstdc++-prettyprinter-update-15-tests-48362
%ifarch %{arm}
rm -f gcc/testsuite/go.test/test/fixedbugs/issue19182.go
%endif
@ -1422,6 +1497,9 @@ then
CONFIG_ARGS="$CONFIG_ARGS --without-annocheck"
CONFIG_ARGS="$CONFIG_ARGS --without-tests"
CONFIG_ARGS="$CONFIG_ARGS --disable-rpath"
CONFIG_ARGS="$CONFIG_ARGS --without-debuginfod"
CONFIG_ARGS="$CONFIG_ARGS --without-clang-plugin"
CONFIG_ARGS="$CONFIG_ARGS --without-llvm-plugin"
comp_dir="%{_builddir}/gcc-%{version}-%{DATE}/obj-%{gcc_target_platform}/gcc/"
ccompiler="%{_builddir}/gcc-%{version}-%{DATE}/obj-%{gcc_target_platform}/gcc/xgcc -B $comp_dir"
@ -1813,9 +1891,9 @@ mv -f %{buildroot}%{_prefix}/%{_lib}/libstdc++*gdb.py* \
%{buildroot}%{_datadir}/gdb/auto-load/%{_prefix}/%{_lib}/
pushd ../libstdc++-v3/python
for i in `find . -name \*.py`; do
touch -r $i %{buildroot}%{_prefix}/share/gcc-%{gcc_major}/python/$i
touch -d @$SOURCE_DATE_EPOCH %{buildroot}%{_prefix}/share/gcc-%{gcc_major}/python/$i
done
touch -r hook.in %{buildroot}%{_datadir}/gdb/auto-load/%{_prefix}/%{_lib}/libstdc++*gdb.py
touch -d @$SOURCE_DATE_EPOCH %{buildroot}%{_datadir}/gdb/auto-load/%{_prefix}/%{_lib}/libstdc++*gdb.py
popd
for f in `find %{buildroot}%{_prefix}/share/gcc-%{gcc_major}/python/ \
%{buildroot}%{_datadir}/gdb/auto-load/%{_prefix}/%{_lib}/ -name \*.py`; do
@ -3594,9 +3672,39 @@ end
%endif
%changelog
* Mon Sep 30 2024 Eduard Abdullin - 11.5.0-2.alma.1
* Mon Sep 15 2025 Eduard Abdullin <eabdullin@almalinux.org> - 11.5.0-11.alma.1
- Debrand for AlmaLinux
* Thu Jul 31 2025 Florian Weimer <fweimer@redhat.com> - 11.5.0-11
- Adjust glibc32 build dependency (RHEL-105072)
* Tue Jul 29 2025 Florian Weimer <fweimer@redhat.com> - 11.5.0-10
- Exception handling performance improvements (RHEL-105072)
- libgcc: Use _dl_find_object to find DWARF data in unwinder
- libgcc: Use lock-free data structures for run-time unwinder registration
* Fri Jun 27 2025 Siddhesh Poyarekar <siddhesh@redhat.com> 11.5.0-9
- Pin modification time for python files to SOURCE_DATE_EPOCH (RHEL-100148).
* Mon Jun 23 2025 Siddhesh Poyarekar <siddhesh@redhat.com> - 11.5.0-8
- Sync libstdc++ pretty printers to latest GTS (RHEL-81975)
* Thu May 29 2025 Joseph Myers <josmyers@redhat.com> - 11.5.0-7
- Fix folding of BIT_NOT_EXPR for POLY_INT_CST (PR 118976, RHEL-90239)
* Wed May 21 2025 David Malcolm <dmalcolm@redhat.com> - 11.5.0-6
- rs6000: Rework ELFv2 support for -fpatchable-function-entry (PR target/99888,
RHEL-75806)
* Fri Feb 7 2025 Marek Polacek <polacek@redhat.com> 11.5.0-5
- rebuild for CVE-2020-11023 (RHEL-78377)
* Mon Jan 27 2025 Marek Polacek <polacek@redhat.com> 11.5.0-4
- revert the PR middle-end/57245 patch (RHEL-76359)
* Tue Jan 21 2025 Marek Polacek <polacek@redhat.com> 11.5.0-3
- honor -frounding-math in real truncation (PR middle-end/57245, RHEL-73749)
* Mon Jul 22 2024 Marek Polacek <polacek@redhat.com> 11.5.0-2
- fix TARGET_CPU_DEFAULT (PR target/105157, RHEL-50037)
- libstdc++: Workaround kernel-headers on s390x-linux (RHEL-50054)