aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal
diff options
context:
space:
mode:
authorCherry Mui <cherryyz@google.com>2025-11-25 13:54:18 -0500
committerCherry Mui <cherryyz@google.com>2026-04-01 11:00:13 -0700
commit46755cb4dea406093296ead4250ff5f7091c04a2 (patch)
tree4558617545a2c0b2dc924d2f95c15655ccb74ee3 /src/cmd/compile/internal
parent8ea160279ff6700282f254d6b74fb02f8a316abe (diff)
downloadgo-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.go1
-rw-r--r--src/cmd/compile/internal/noder/noder.go9
-rw-r--r--src/cmd/compile/internal/noder/reader.go7
-rw-r--r--src/cmd/compile/internal/noder/writer.go33
-rw-r--r--src/cmd/compile/internal/ssagen/abi.go3
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