Skip to content

edg: -C on the archive too, and the finalization pair EDG leaves open - #617

Merged
staalmannen merged 1 commit into
mainfrom
claude/upgrade-ape-c-library-mmZGd
Oct 8, 2026
Merged

staalmannen merged 1 commit into
mainfrom
claude/upgrade-ape-c-library-mmZGd

Conversation

@staalmannen

Copy link
Copy Markdown
Owner

The cmd/edg link is down to two things and only one of them is ours.

lib/edg built libedg.a with CFLAGS=-c while cmd/edg has had -c -C since the round that found it. An archive takes duplicate members silently, so the archive looked clean; the clash arrives only when the link pulls two members defining one vague-linkage entity. 6l answered

(295) DATA _ZTSv+0(SB)/1,$118
__tourite_needs_stdio_exit: multiple initialization

for _ZTSv, _ZTIv, _ZTSDn and _ZTIDn -- the void and decltype(nullptr) type strings and type_info objects -- each of which throw.c and typeinfo.c both define, counted rather than guessed. asm.c:548 suppresses that on sym->dupok and obj.c:908 sets it from the AGLOBL's DUPOK bit, which -C is the only way to ask for. dupok is a property of the Sym, so one side carrying it would have been enough and neither did. (The symbol in that message is stale diag context and names nothing: read the DATA line above it.)

The remaining undefined is __cxa_finalize, and it is a gap EDG leaves to the platform rather than one this tree opened: include_c++/cxxabi.h declares the finalization pair and lib_src defines neither, because everywhere else the C++ runtime supplies them. cxa_atexit.c and dso_handle.c are the two hand-written files in lib/edg, deliberately not in libap -- a C++ ABI name in the library every program links is the reject shape -- and compiled without -C, since marking a hand-written file's symbols dupok would turn a second definition of __cxa_atexit into first-wins silence.

They follow Itanium 3.3.5.3 rather than approximating it: the entry is removed BEFORE it is called and the list re-scanned afterwards, because a destructor may register more. A plain descending for loop over the list passes the ordering test and fails both, which is measured rather than argued -- the naive version replicated beside the real one gives 4 failures naming sections 3, 4a, 4b and 5b.

__dso_handle is its own file because glibc's crtbeginS.o already defines one, so with both in a single object the host cross-check does not link at all. That collision is the measurement: the handle belongs to the C startup and the finalizers to the C++ runtime, and the host says so by owning exactly one of the two. cpfe references neither -- __dso_handle appears in src/lower_init.c only as a string literal, because cpfe emits references to it in the C it generates.

cxaatexit-test.c runs unchanged on both machines, 0 failures on gcc.

And the bacon recipe recorded here was wrong in both halves. -c cc rather than -c pcc: bacon's generated Makefile names $stem.o, and pcc.c:75 is objext = (strcmp(prog,"cc")==0) ? "o" : ot->o while 9src/cc.c is one line including pcc.c -- cc and pcc are one binary and the name chooses the extension, so -c cc is the same compiler under the name whose output every Makefile expects. And BACON_IN_DOCKER=true rather than </dev/null: both prompt sites are already guarded by that variable, while closing stdin does not make the prompt take its default, it makes bacon die -- the generated __b2c__input calls getdelim, -1 at EOF falls into the arm that raises "Error opening file", and compiling those four lines verbatim on glibc with stdin on /dev/null gives the same arm. Upstream's, on every platform.

Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs

The cmd/edg link is down to two things and only one of them is ours.

lib/edg built libedg.a with CFLAGS=-c while cmd/edg has had -c -C
since the round that found it.  An archive takes duplicate members
silently, so the archive looked clean; the clash arrives only when
the link pulls two members defining one vague-linkage entity.  6l
answered

  (295) DATA _ZTSv+0(SB)/1,$118
  __tourite_needs_stdio_exit: multiple initialization

for _ZTSv, _ZTIv, _ZTSDn and _ZTIDn -- the void and decltype(nullptr)
type strings and type_info objects -- each of which throw.c and
typeinfo.c both define, counted rather than guessed.  asm.c:548
suppresses that on sym->dupok and obj.c:908 sets it from the AGLOBL's
DUPOK bit, which -C is the only way to ask for.  dupok is a property
of the Sym, so one side carrying it would have been enough and
neither did.  (The symbol in that message is stale diag context and
names nothing: read the DATA line above it.)

The remaining undefined is __cxa_finalize, and it is a gap EDG leaves
to the platform rather than one this tree opened: include_c++/cxxabi.h
declares the finalization pair and lib_src defines neither, because
everywhere else the C++ runtime supplies them.  cxa_atexit.c and
dso_handle.c are the two hand-written files in lib/edg, deliberately
not in libap -- a C++ ABI name in the library every program links is
the reject shape -- and compiled without -C, since marking a
hand-written file's symbols dupok would turn a second definition of
__cxa_atexit into first-wins silence.

They follow Itanium 3.3.5.3 rather than approximating it: the entry
is removed BEFORE it is called and the list re-scanned afterwards,
because a destructor may register more.  A plain descending for loop
over the list passes the ordering test and fails both, which is
measured rather than argued -- the naive version replicated beside
the real one gives 4 failures naming sections 3, 4a, 4b and 5b.

__dso_handle is its own file because glibc's crtbeginS.o already
defines one, so with both in a single object the host cross-check
does not link at all.  That collision is the measurement: the handle
belongs to the C startup and the finalizers to the C++ runtime, and
the host says so by owning exactly one of the two.  cpfe references
neither -- __dso_handle appears in src/lower_init.c only as a string
literal, because cpfe emits references to it in the C it generates.

cxaatexit-test.c runs unchanged on both machines, 0 failures on gcc.

And the bacon recipe recorded here was wrong in both halves.  -c cc
rather than -c pcc: bacon's generated Makefile names $stem.o, and
pcc.c:75 is objext = (strcmp(prog,"cc")==0) ? "o" : ot->o while
9src/cc.c is one line including pcc.c -- cc and pcc are one binary
and the name chooses the extension, so -c cc is the same compiler
under the name whose output every Makefile expects.  And
BACON_IN_DOCKER=true rather than </dev/null: both prompt sites are
already guarded by that variable, while closing stdin does not make
the prompt take its default, it makes bacon die -- the generated
__b2c__input calls getdelim, -1 at EOF falls into the arm that raises
"Error opening file", and compiling those four lines verbatim on
glibc with stdin on /dev/null gives the same arm.  Upstream's, on
every platform.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs
@staalmannen
staalmannen merged commit f6e1968 into main Oct 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants