← board

An import pays for storage it cannot use

The reservation happens at declaration time and the import decision is a fold over every declaration of the name, so the allocator cannot know. By the time SymObjDataExternOnly is final the slot exists.

Measured with the same program either side of one keyword:

extern int Big[1000];  int get(void){return Big[3];}    bss=42156B
       int Big[1000];  int get(void){return Big[3];}    bss=42156B

Harmless in the sense that matters — the writer routes every reference to the UND symbol, so nothing reads the dead slot — and it is why this is prio 25 rather than a bug. It costs object size, which is the same currency as [[feature-a-every-emit-obj-object-links-its-own-full-copy-of-crtl-so-n-objects-cost-n-runtimes]] and worth folding into whatever measurement that one does.

The shape of a fix

Not "do not allocate": the fold is not final until the translation unit ends, and a name that looked like an import can still become a definition (C 6.9.2's tentative definition, which is busybox's dominant shape). So it is a RECLAIM at end of unit, or a second .bss pass that lays out only the symbols that survived as definitions. The second is the honest one and it is not small.

Worth checking first whether real inputs make it worth anything: busybox's libbb.h declares a lot of names into 41 translation units, so the multiplier may be larger than the single-file number suggests.

Measured 2026-09-18 (frankB) — the ticket's own gating question, answered: NOT worth a reclaim pass, and ZERO on ESP

This ticket says "Worth checking first whether real inputs make it worth anything" and names busybox as the input to check. Checked. The answer is no, and it is no for two independent reasons.

Nothing could answer it before, so an instrument was built first

The reservation is invisible from outside the compiler: the UND symbol carries st_size 0 and NOTYPE, so readelf cannot see it. The size lives only in SymAllocSize. PXXDBG=a.impwaste (elfwriter.inc, ObjReportImportedDataWaste) now reports, at write time and through the same ObjDataIsImport predicate the writers walk: every import with its offset, size and referenced flag, and a total in two bounds.

Two bounds because the sum of sizes is NOT the answer — each slot is aligned, so removing an import frees its padding too. The sum is a lower bound; the offset SPAN is an upper bound, and only a true one when nothing else was allocated inside it, which is why others-in-span is printed rather than assumed.

Positive control, run: extern int Big[1000]; reports 4000, the same file with the extern removed reports 0, and bss=42700 is identical in both — so the instrument discriminates the thing the whole ticket is about, on the ticket's own fixture.

ESP: zero, by construction, on both profiles

--emit-obj, xtensa/riscv32   REFUSED  (ObjRefuseEspDataImports, elfwriter.inc)
  "an imported variable (X) is not supported for xtensa/riscv32 objects:
   the reference would relocate into this object's own .bss and read zero"
executable, any target        REFUSED  (pasparser)
  "external on a variable needs --emit-obj: in an executable there is no
   import for it to bind to, so it would allocate local storage and read zero"

So an ESP program cannot contain this defect. Measured too, not only reasoned: test/esp_pal_fdsem_baseline.pas built --emit-obj --target=xtensa --platform=esp reports 0 imports, 0 bytes, and lib/** declares no external variable at all. Under the 2026-09-18 ESP SRAM re-rank this ticket is worth nothing — it should not be raised on that basis.

busybox: ~241 bytes per translation unit

--emit-obj, -include include/autoconf.h -I. -Iinclude -Ilibbb:

TU imports sum of sizes span others in span bss
coreutils/echo.c 33 102 241 0 91268
libbb/xfuncs.c 33 102 241 0 91284
networking/wget.c 33 102 241 0 91324
coreutils/cat.c 34 103 44641 43 91276
editors/sed.c 34 103 44641 43 91388
coreutils/ls.c 34 103 44641 43 91320

Three TUs have others-in-span=0, so for those the span is EXACT: 241 bytes, of which 102 is declared size and 139 is alignment padding. The other three differ by one symbol, bb_common_bufsiz1, which lands at offset 83464 with 43 unrelated allocations between — their span is therefore meaningless and the truth is ~242.

0.26% of the object's .bss. The multiplier this ticket hoped for is not there: libbb.h does declare a lot of names into 41 translation units, but almost every one is extern const char x[] — an INCOMPLETE array, which reserves one byte. The cost is dominated by 8-byte alignment on 1-byte slots, not by the declarations' sizes. Across 41 separate objects it is ~9.9 KB in total and ~241 B in any one of them.

Conclusion

Left at prio 25 and not implemented. The fix this ticket describes — a second .bss pass laying out only the symbols that survived as definitions, or a reclaim at end of unit — would have to remap every GlobFix.BSSoff, which stores an OFFSET and not a symbol index, so the remap is a DceNewOff-shaped compaction over a table with no symbol association. That is the same machinery as dce.inc's code compaction and carries the same class of risk, for 241 bytes per object on the only population that has the defect at all.

What this section changes is that the question is now ANSWERED rather than open: a future reader does not have to re-derive whether it is worth doing, and if the population changes — a real program with large extern arrays, which is the shape that would move the number — PXXDBG=a.impwaste measures it in one run.