diff options
| author | Cherry Mui <cherryyz@google.com> | 2025-11-25 13:54:18 -0500 |
|---|---|---|
| committer | Cherry Mui <cherryyz@google.com> | 2026-04-01 11:00:13 -0700 |
| commit | 46755cb4dea406093296ead4250ff5f7091c04a2 (patch) | |
| tree | 4558617545a2c0b2dc924d2f95c15655ccb74ee3 /src/cmd/compile/internal | |
| parent | 8ea160279ff6700282f254d6b74fb02f8a316abe (diff) | |
| download | go-46755cb4dea406093296ead4250ff5f7091c04a2.tar.xz | |
cmd/compile, cmd/link: add linknamestd directive for std-only linknames
In the standard library, there are a number of linknames, for
sharing symbols within the standard library. They are not supposed
to be accessed externally. But currently there is no good
mechanism to prevent that. In the linker we have a blocklist of
linknames, which forbids linkname references other than explicitly
allowed packages. The blocklist is manually maintained, requiring
periodic manual update.
To move away from that manually maintained blocklist, this CL
introduces a new directive, linknamestd, that marks a linkname
for use within the standard library only. The linker will allow
references within the standard library and forbid others.
For a proof of concept, runtime.coroswitch is removed from the
blocklist, and replaced with linknamestd. An external reference to
it is still disallowed by the linker, as tested with
cmd/link.TestCheckLinkname with testdata/linkname/coro.go.
Change-Id: I0d0f8746b8835d8cdcfc3ff835d22a551da5f038
Reviewed-on: https://go-review.googlesource.com/c/go/+/749942
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Diffstat (limited to 'src/cmd/compile/internal')
| -rw-r--r-- | src/cmd/compile/internal/noder/linker.go | 1 | ||||
| -rw-r--r-- | src/cmd/compile/internal/noder/noder.go | 9 | ||||
| -rw-r--r-- | src/cmd/compile/internal/noder/reader.go | 7 | ||||
| -rw-r--r-- | src/cmd/compile/internal/noder/writer.go | 33 | ||||
| -rw-r--r-- | src/cmd/compile/internal/ssagen/abi.go | 3 |
5 files changed, 39 insertions, 14 deletions
diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go index 51b03a1897..df1e248398 100644 --- a/src/cmd/compile/internal/noder/linker.go +++ b/src/cmd/compile/internal/noder/linker.go @@ -338,6 +338,7 @@ func (l *linker) linkname(w *pkgbits.Encoder, name *ir.Name) { linkname := name.Sym().Linkname if !l.lsymIdx(w, linkname, name.Linksym()) { w.String(linkname) + w.Bool(name.Linksym().IsLinknameStd()) } } diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index 973d917784..4e48126e82 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -103,9 +103,10 @@ type noder struct { err chan syntax.Error } -// linkname records a //go:linkname directive. +// linkname records a //go:linkname or //go:linknamestd directive. type linkname struct { pos syntax.Pos + std bool local string remote string } @@ -273,10 +274,10 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P } } - case strings.HasPrefix(text, "go:linkname "): + case strings.HasPrefix(text, "go:linkname "), strings.HasPrefix(text, "go:linknamestd "): f := strings.Fields(text) if !(2 <= len(f) && len(f) <= 3) { - p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"}) + p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("usage: //%s localname [linkname]", f[0])}) break } // The second argument is optional. If omitted, we use @@ -294,7 +295,7 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P } else { panic("missing pkgpath") } - p.linknames = append(p.linknames, linkname{pos, f[1], target}) + p.linknames = append(p.linknames, linkname{pos, f[0] == "go:linknamestd", f[1], target}) case text == "go:embed", strings.HasPrefix(text, "go:embed "): args, err := parseGoEmbed(text[len("go:embed"):]) diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 00710775d6..ebb5043a05 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1277,6 +1277,7 @@ func (r *reader) linkname(name *ir.Name) { lsym.Set(obj.AttrIndexed, true) } else { linkname := r.String() + std := r.Bool() sym := name.Sym() sym.Linkname = linkname if sym.Pkg == types.LocalPkg && linkname != "" { @@ -1286,7 +1287,11 @@ func (r *reader) linkname(name *ir.Name) { // corresponding packages). So we can tell in which package // the linkname is used (pulled), and the linker can // make a decision for allowing or disallowing it. - sym.Linksym().Set(obj.AttrLinkname, true) + if std { + sym.Linksym().Set(obj.AttrLinknameStd, true) + } else { + sym.Linksym().Set(obj.AttrLinkname, true) + } } } } diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 9e0f4fc9c8..027d7e0636 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -86,8 +86,11 @@ type pkgWriter struct { typDecls map[*types2.TypeName]typeDeclGen // linknames maps package-scope objects to their linker symbol name, - // if specified by a //go:linkname directive. - linknames map[types2.Object]string + // if specified by a //go:linkname or //go:linknamestd directive. + linknames map[types2.Object]struct { + remote string + std bool + } // cgoPragmas accumulates any //go:cgo_* pragmas that need to be // passed through to cmd/link. @@ -114,7 +117,10 @@ func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info, otherInfo ma funDecls: make(map[*types2.Func]*syntax.FuncDecl), typDecls: make(map[*types2.TypeName]typeDeclGen), - linknames: make(map[types2.Object]string), + linknames: make(map[types2.Object]struct { + remote string + std bool + }), } } @@ -1175,7 +1181,9 @@ func (w *writer) varExt(obj *types2.Var) { func (w *writer) linkname(obj types2.Object) { w.Sync(pkgbits.SyncLinkname) w.Int64(-1) - w.String(w.p.linknames[obj]) + info := w.p.linknames[obj] + w.String(info.remote) + w.Bool(info.std) } func (w *writer) pragmaFlag(p ir.PragmaFlag) { @@ -2799,26 +2807,33 @@ func (pw *pkgWriter) collectDecls(noders []*noder) { pw.cgoPragmas = append(pw.cgoPragmas, p.pragcgobuf...) for _, l := range p.linknames { + directive := "go:linkname" + if l.std { + directive = "go:linknamestd" + } if !file.importedUnsafe { - pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") + pw.errorf(l.pos, "//%s only allowed in Go files that import \"unsafe\"", directive) continue } if strings.Contains(l.remote, "[") && strings.Contains(l.remote, "]") { - pw.errorf(l.pos, "//go:linkname reference of an instantiation is not allowed") + pw.errorf(l.pos, "//%s reference of an instantiation is not allowed", directive) continue } switch obj := pw.curpkg.Scope().Lookup(l.local).(type) { case *types2.Func, *types2.Var: if _, ok := pw.linknames[obj]; !ok { - pw.linknames[obj] = l.remote + pw.linknames[obj] = struct { + remote string + std bool + }{l.remote, l.std} } else { - pw.errorf(l.pos, "duplicate //go:linkname for %s", l.local) + pw.errorf(l.pos, "duplicate //%s for %s", directive, l.local) } default: if types.AllowsGoVersion(1, 18) { - pw.errorf(l.pos, "//go:linkname must refer to declared function or variable") + pw.errorf(l.pos, "//%s must refer to declared function or variable", directive) } } } diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go index 0e8dbd9445..107e48c8c9 100644 --- a/src/cmd/compile/internal/ssagen/abi.go +++ b/src/cmd/compile/internal/ssagen/abi.go @@ -168,6 +168,9 @@ func (s *SymABIs) GenABIWrappers() { if sym.Linksym().IsLinkname() { sym.LinksymABI(fn.ABI).Set(obj.AttrLinkname, true) } + if sym.Linksym().IsLinknameStd() { + sym.LinksymABI(fn.ABI).Set(obj.AttrLinknameStd, true) + } } // If cgo-exported, add the definition ABI to the cgo |
