← board

static stopped at the data side

ObjDataIsLocal has read SymCStaticLink since the object-linkage work landed. ObjProcIsExported was Result := ProcCdecl[procIdx] and asked nothing about linkage, so static int helper(int) came out T where gcc emits t — measured on a two-file repro before anything else.

That is the double-case rule failing in the ordinary way: one arm was fixed, the sibling was never grepped for.

How it was found

By attempting the target, not by reading the backlog. The 26-applet busybox userland compiled to 82 objects and the link died:

multiple definition of `bb_ascii_isalnum'
multiple definition of `bb_ascii_isxdigit'
multiple definition of `bb_ascii_tolower'
multiple definition of `bb_ascii_toupper'
multiple definition of `bb_strtoi32'
multiple definition of `bb_strtol'
multiple definition of `bb_strtou32'
multiple definition of `bb_strtoul'
multiple definition of `is_tty_secure'
multiple definition of `new_tls_state'

Every one is static ALWAYS_INLINE in include/libbb.h or include/xatonum.h, and every translation unit includes them.

The harness could not say that until it was fixed too. Its link-failure report grepped only for undefined reference, so a link that died on multiple definitions printed the FAIL line and no cause at all. A diagnostic silent on half its population is the half you spend the hour on.

The fix

ProcCStaticLink, the proc-side twin of SymCStaticLink:

hdrStaticBody could not stand in for it: that asks the narrower question of a bodied static reached through a header import, and is False for the ordinary case of a static defined in the .c the user named.

No new symbol group was needed. A defined-but-unexported proc already lands in the LOCAL block ahead of firstGlobal — where every non-cdecl Pascal routine has always gone.

Why LOCAL and not WEAK

Weak also links, and is wrong. The linker would keep ONE body and both translation units would call it; internal linkage means each has its own. test/c_obj_static_link_{a,b}.c define the same two static names with DIFFERENT bodies for exactly this reason, so the OUTPUT discriminates: a 11 / b 2200 under internal linkage, both rows collapsing onto one body under a weak fix. gcc building the same two sources is the oracle, and the symbol-binding assertion is aimed both ways — a_probe must still come out GLOBAL, or a writer that localised everything would pass.

Verified

Log