From db8e3fc16007c55368b8a688deafcb1342de0e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Mon, 20 Jul 2026 16:28:00 +0200 Subject: [PATCH] docs: record the deferred GOOBJ external-symbols decision --- docs/DEFERRED.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 docs/DEFERRED.md diff --git a/docs/DEFERRED.md b/docs/DEFERRED.md new file mode 100644 index 0000000..813a47e --- /dev/null +++ b/docs/DEFERRED.md @@ -0,0 +1,56 @@ +# Deferred decisions + +Design decisions deliberately postponed, with enough context to pick them up +again without re-deriving the analysis. Each entry records what is deferred, +why, the options on the table, and the trigger that should reopen it. + +--- + +## GOOBJ external (cross-package) symbol references + +**Status:** deferred (v0.15.0, 2026-08-02). The GOOBJ emitter resolves only +symbols defined in the file being assembled; a reference to any other symbol +is rejected. + +**Why it is deferred.** GOOBJ symbol references are *positional*: a +reference is a `{PkgIdx, SymIdx}` pair, where `SymIdx` is the index of the +symbol in the *referenced package's* symbol-definition table. That ordering +is not derivable from the reference site — it lives in the referenced +package's gc export data (the iexport binary format, which evolves with the +toolchain). `cmd/asm` reads it with `cmd/internal` readers gasm cannot +import, so emitting external references means either parsing export data +ourselves or taking a dependency that does. + +**What works today.** Single-package objects: every symbol the file defines +(as `TEXT` or `GLOBL`, static or exported) and every reference to them. +This covers the production use case — the go-flac / go-lz4 kernels carry no +`FUNCDATA`/`PCDATA`, hence no references into `runtime`, and the Go side +references the assembly symbols, never the reverse. Such a package builds +with its assembly object replaced by a gasm-emitted one. + +**The options, when we return.** + +1. **`golang.org/x/tools/go/gcexportdata` as a production dependency.** + The straightforward path: read each imported package's export file + (paths from `-importcfg` or `go list -export`), assign symbol indices in + its symbol order, write `PkgIndex`/`Autolib` entries (fingerprints from + the export files' build IDs) and positional references. Robust across + toolchain versions — `x/tools` tracks the format. **Cost:** the first + production dependency beyond the standard library, an explicit deviation + from the "production code depends only on the standard library" + principle in the README. Requires the user's explicit agreement. +2. **A minimal iexport parser of our own.** Preserves self-containment. + Substantial effort and inherently fragile: the format is an internal + contract that changes with Go releases, so the parser needs a + version-gated fallback and regression tests against several toolchains. +3. **Shell out to the toolchain for symbol metadata.** Consistent with the + existing GOOBJ preamble probe (which already runs `go tool asm`), but no + toolchain command exposes a package's symbols *in definition-index + order* — `go tool nm` sorts differently — so this does not solve the + core problem on its own; it would only feed option 1 or 2. + +**Trigger to reopen.** An assembly file that needs a cross-package +reference — in practice `FUNCDATA $…, runtime·…(SB)` (stack maps / GC +metadata written in assembly), or any kernel that calls into another +package directly. Until then, option 3's limitation is moot and the +single-package emitter suffices.