On 11/6/26 1:46 PM, Camm Maguire wrote:

Greetings, again!

Raymond Toy<[email protected]> writes:

Anyway, perhaps Claude can think of something and save us some time.

I very briefly skimmed the log. Not sure what I’m looking for or if AI could 
help figure out the underlying option.

I personally don’t care about parallel builds, if we’re talking about
building gcl. It is kind of slow, but I rarely ever build gcl. I guess
that’s different for you.

Alas, the issue here is not build time, but build reproducibility,
an upcoming Debian requirement.   I have about 6 months to figure out
why setting SOURCE_DATE_EPOCH to a known constant and repeating the
build does not generate the same result.  Can Claude help with that?
That’s a more concrete question that I could ask Claude about. I’ll let you know if I get anything or get stuck.

Well, that was quick. Here is what Claude said. Don’t know if that helps you in any way. Feel free to ask more questions. And you do know that Claude has a free tier, right? It’s pretty capable and can do a lot before you run into limits.

|SOURCE_DATE_EPOCH| only fixes timestamps. Most of GCL’s non-determinism comes from how its images are made: the final |saved_gcl| / |saved_ansi_gcl| is a dump of a running process’s memory. Anything that varies between runs and lands in the heap ends up in the binary.


   Usual culprits

1.

   *The image is an unexec’d heap.*
   GCL builds the system by starting a raw image, loading compiled |.o|
   files with its own loader, and dumping memory with |save-system|
   (unexec). Byte-identical output requires every allocation, GC, and
   stray value to come out the same in both runs. That’s a much
   stronger requirement than deterministic compilation.

2.

   *Address-space variation.*
   GCL tries to disable ASLR (|personality(ADDR_NO_RANDOMIZE)| and
   re-exec), but layout can still shift. The stack base moves with the
   size of the environment and argv, and |brk|/|mmap| placement can
   differ with kernel settings or sandboxing (seccomp, containers,
   |setarch|). Once addresses differ, every pointer in the heap differs
   too.

3.

   *Conservative GC.*
   GCL scans the C stack conservatively. Stale words on the stack,
   which depend on environment size, |PWD|, and similar, can keep
   different objects alive. Collections also happen at slightly
   different points. The result is different live sets and different
   free-list layouts in the dumped heap.

4.

   *Address-dependent ordering.*
   EQ/EQL hash tables hash on addresses, so their bucket layout, and
   anything built by iterating over them, varies with allocation addresses.

5.

   *Uninitialized or unzeroed memory.*
   Freed or padding regions of the heap and of the unexec’d data
   segment can contain leftover bytes, and those get dumped verbatim.

6.

   *Embedded environment data that isn’t a timestamp:*

     * absolute build paths, such as the system directory,
       |si::*system-directory*|, and source paths in compiled code and
       debug info
     * hostname, user, or |uname| output captured at configure time
     * PIDs in compiler temp file names (|gazonk_<pid>_…|), which can
       leak into generated C, init symbol names, or object files
     * the gensym counter and |*random-state*| as they stand at save
       time, which depend on what happened during the build
7.

   *Toolchain nondeterminism.*
   This is less likely with a modern GCC and binutils, but there can
   still be |-frandom-seed| related symbol names and unsorted archive
   members.


   Narrowing it down

 *

   Run |diffoscope| on two builds. Diffs confined to the saved images,
   with identical |.o| files, point to heap/GC/ASLR causes (1–5). Diffs
   in the |.o| or |.c| files point to paths, PIDs, or the compiler (6–7).

 *

   Build twice in the same directory, with the same user, hostname, and
   environment (|env -i| with a fixed env), under |setarch -R|. If the
   results then match, the remaining variation is environmental rather
   than intrinsic.

 *

   Watch for |gazonk| PIDs and absolute paths:

   |strings saved_gcl | grep -E 'gazonk|/build/' |

Debian’s reproducible-builds effort has looked at GCL for exactly these reasons. As far as I know it has not been fully reproducible there either, largely because of the unexec/heap-dump issue.

&#8203;

Reply via email to