https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124811
Richard Biener <rguenth at gcc dot gnu.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
Assignee|rguenth at gcc dot gnu.org |unassigned at gcc dot
gnu.org
CC| |jakub at gcc dot gnu.org,
| |jason at gcc dot gnu.org,
| |rguenth at gcc dot gnu.org
Status|ASSIGNED |NEW
--- Comment #7 from Richard Biener <rguenth at gcc dot gnu.org> ---
So a difference with/without PCH is to be expected as the slot order is
dependent on the insertion order when collisions are present (and on the
history of table re-sizings/rehashings). Still the insertion order upon PCH
read should be the same.
So the issue is likely different insertion order from frontend hash
traversals after PCH read?
Also in dwarf2out we sort filenames with file_info_cmp which can return 0
for files with the same directory prefix but we're using qsort, not
gcc_stablesort for those. Our own qsort should be at least deterministic
though.
I'm not sure we should paper over C++ frontend issues in dwarf2out.
I can see at least cp/tree.cc:list_hash_table and cp/decl.cc:typename_htab
and cp/constraint.cc:atom_cache
When not using PCH there's the same randomness but given we do not traverse
above tables the order of insertions is consistent. Now, with PCH the
randomness of two runs get mixed. An LLM explains:
2. Why PCH-Enabled Builds Leak Randomness
When you compile main.c using two different precompiled headers (PCH A and
PCH B) generated
from the exact same header.h under different ASLR layouts, the randomization
escapes.
Step 1: Restoring Pre-populated Tables
The pre-populated C++ front-end hash tables (like typename_htab,
decl_specializations,
type_specializations, and list_hash_table) are restored from the .gch file.
* In Run A (using PCH A), these tables start with the slots and collision
probe patterns from
ASLR layout A.
* In Run B (using PCH B), they start with the slots and collision probe
patterns from ASLR
layout B.
Step 2: Iteration Over Pointer-Dependent Tables
Unlike typename_htab, some of these pre-populated tables are traversed by the
C++ front-end to
perform compiler actions.
For example, in gcc/cp/pt.cc, template specializations are iterated using
iterators to perform
downstream processing or instantiations:
1 spec_hash_table *table = decls_p ? decl_specializations
2 : type_specializations;
3 spec_hash_table::iterator end (table->end ());
4 for (spec_hash_table::iterator iter (table->begin ()); iter != end;
++iter)
Because the iterator loops linearly over the slots (m_entries), the order in
which
specializations are visited and processed is determined by their physical
slot index. Since
the slots are different due to PCH A's vs. PCH B's ASLR layouts, the compiler
processes and
instantiates these templates in a different chronological order.
Step 3: UID Divergence and Chronological Shifts
Because template instantiations and type creations happen in a different
chronological
sequence:
* New types created during these instantiations are assigned TYPE_UIDs in a
different order.
* Files and directories are referenced and registered in file_table (via
lookup_filename) in
a different order.
* The physical slots in file_table and the chronological sequence of strings
added to
debug_line_str_hash diverge.
Step 4: The Unstable Sort Cascade
Finally, during debug info generation:
1. file_table is traversed, producing an array of files in its (now
randomized) slot order.
2. This array is sorted using qsort with file_info_cmp.
3. Because file_info_cmp is unstable (it returns 0 for different files
sharing the same
directory prefix), qsort preserves the relative randomized slot order of
these files.
4. The files are then sequentially inserted into debug_line_str_hash in this
randomized
sorted order.
5. This leads to different slot layouts in debug_line_str_hash, resulting in
a different
traversal order when emitting .debug_line_str, making the final debug
info
non-reproducible.