Flag.CompilingRuntime = true
}
+ Ctxt.Std = Flag.Std
+
// Three inputs govern loop iteration variable rewriting, hash, experiment, flag.
// The loop variable rewriting is:
// IF non-empty hash, then hash determines behavior (function+line match) (*)
nbitmap = 2
}
lsym := base.Ctxt.Lookup(fn.LSym.Name + ".args_stackmap")
+ lsym.Set(obj.AttrLinkname, true) // allow args_stackmap referenced from assembly
off := objw.Uint32(lsym, 0, uint32(nbitmap))
off = objw.Uint32(lsym, off, uint32(bv.N))
off = objw.BitVec(lsym, off, bv)
// offsets to dispatch arguments, which currently using ABI0
// frame layout. Pin it to ABI0.
fn.ABI = obj.ABI0
+ // Propagate linkname attribute, which was set on the ABIInternal
+ // symbol.
+ if sym.Linksym().IsLinkname() {
+ sym.LinksymABI(fn.ABI).Set(obj.AttrLinkname, true)
+ }
}
// If cgo-exported, add the definition ABI to the cgo
if runtime.Compiler != "gccgo" {
// Check the list is in dependency order.
tg.run("list", "-deps", "math")
- want := "internal/cpu\nunsafe\nmath/bits\nmath\n"
+ want := "unsafe\ninternal/cpu\nmath/bits\nmath\n"
out := tg.stdout.String()
if !strings.Contains(out, "internal/cpu") {
// Some systems don't use internal/cpu.
_ // was ObjFlagNeedNameExpansion
ObjFlagFromAssembly // object is from asm src, not go
ObjFlagUnlinkable // unlinkable package (linker will emit an error)
+ ObjFlagStd // standard library package
)
// Sym.Flag
SymFlagDict
SymFlagPkgInit
SymFlagLinkname
+ SymFlagABIWrapper
)
// Returns the length of the name of the symbol.
func (s *Sym) IsDict() bool { return s.Flag2()&SymFlagDict != 0 }
func (s *Sym) IsPkgInit() bool { return s.Flag2()&SymFlagPkgInit != 0 }
func (s *Sym) IsLinkname() bool { return s.Flag2()&SymFlagLinkname != 0 }
+func (s *Sym) ABIWrapper() bool { return s.Flag2()&SymFlagABIWrapper != 0 }
func (s *Sym) SetName(x string, w *Writer) {
binary.LittleEndian.PutUint32(s[:], uint32(len(x)))
func (r *Reader) Shared() bool { return r.Flags()&ObjFlagShared != 0 }
func (r *Reader) FromAssembly() bool { return r.Flags()&ObjFlagFromAssembly != 0 }
func (r *Reader) Unlinkable() bool { return r.Flags()&ObjFlagUnlinkable != 0 }
+func (r *Reader) Std() bool { return r.Flags()&ObjFlagStd != 0 }
InParallel bool // parallel backend phase in effect
UseBASEntries bool // use Base Address Selection Entries in location lists and PC ranges
IsAsm bool // is the source assembly language, which may contain surprising idioms (e.g., call tables)
+ Std bool // is standard library package
// state for writing objects
Text []*LSym
if ctxt.IsAsm {
flags |= goobj.ObjFlagFromAssembly
}
+ if ctxt.Std {
+ flags |= goobj.ObjFlagStd
+ }
h := goobj.Header{
Magic: goobj.Magic,
Fingerprint: ctxt.Fingerprint,
const cutoff = int64(2e9) // 2 GB (or so; looks better in errors than 2^31)
func (w *writer) Sym(s *LSym) {
+ name := s.Name
abi := uint16(s.ABI())
if s.Static() {
abi = goobj.SymABIstatic
if s.IsPkgInit() {
flag2 |= goobj.SymFlagPkgInit
}
- if s.IsLinkname() || w.ctxt.IsAsm { // assembly reference is treated the same as linkname
+ if s.IsLinkname() || (w.ctxt.IsAsm && name != "") || name == "main.main" {
+ // Assembly reference is treated the same as linkname,
+ // but not for unnamed (aux) symbols.
+ // The runtime linknames main.main.
flag2 |= goobj.SymFlagLinkname
}
- name := s.Name
+ if s.ABIWrapper() {
+ flag2 |= goobj.SymFlagABIWrapper
+ }
if strings.HasPrefix(name, "gofile..") {
name = filepath.ToSlash(name)
}
func (ctxt *Link) loadlib() {
var flags uint32
+ if *flagCheckLinkname {
+ flags |= loader.FlagCheckLinkname
+ }
switch *FlagStrictDups {
case 0:
// nothing to do
FlagS = flag.Bool("s", false, "disable symbol table")
flag8 bool // use 64-bit addresses in symbol table
flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
+ flagCheckLinkname = flag.Bool("checklinkname", false, "check linkname symbol references")
FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
FlagDebugTextSize = flag.Int("debugtextsize", 0, "debug text section max size")
flagDebugNosplit = flag.Bool("debugnosplit", false, "dump nosplit call graph")
const (
// Loader.flags
FlagStrictDups = 1 << iota
+ FlagCheckLinkname
)
func NewLoader(flags uint32, reporter *ErrorReporter) *Loader {
}
// Non-package (named) symbol.
- if osym.IsLinkname() && r.DataSize(li) == 0 {
- // This is a linknamed "var" "reference" (var x T with no data and //go:linkname x).
- // Check if a linkname reference is allowed.
- // Only check references (pull), not definitions (push, with non-zero size),
- // so push is always allowed.
- // Linkname is always a non-package reference.
- checkLinkname(r.unit.Lib.Pkg, name)
- }
// Check if it already exists.
oldi, existed := l.symsByName[ver][name]
if !existed {
l *Loader
hashed64Syms map[uint64]symAndSize // short hashed (content-addressable) symbols, keyed by content hash
hashedSyms map[goobj.HashType]symAndSize // hashed (content-addressable) symbols, keyed by content hash
+
+ linknameVarRefs []linknameVarRef // linknamed var refererces
+}
+
+type linknameVarRef struct {
+ pkg string // package of reference (not definition)
+ name string
+ sym Sym
}
// Preload symbols of given kind from an object.
}
gi := st.addSym(name, v, r, i, kind, osym)
r.syms[i] = gi
+ if kind == nonPkgDef && osym.IsLinkname() && r.DataSize(i) == 0 && strings.Contains(name, ".") {
+ // This is a linknamed "var" "reference" (var x T with no data and //go:linkname x).
+ // We want to check if a linkname reference is allowed. Here we haven't loaded all
+ // symbol definitions, so we don't yet know all the push linknames. So we add to a
+ // list and check later after all symbol defs are loaded. Linknamed vars are rare,
+ // so this list won't be long.
+ // Only check references (pull), not definitions (push, with non-zero size),
+ // so push is always allowed.
+ // This use of linkname is usually for referencing C symbols, so allow symbols
+ // with no "." in its name (not a regular Go symbol).
+ // Linkname is always a non-package reference.
+ st.linknameVarRefs = append(st.linknameVarRefs, linknameVarRef{r.unit.Lib.Pkg, name, gi})
+ }
if osym.Local() {
l.SetAttrLocal(gi, true)
}
st.preloadSyms(r, hashedDef)
st.preloadSyms(r, nonPkgDef)
}
+ for _, vr := range st.linknameVarRefs {
+ l.checkLinkname(vr.pkg, vr.name, vr.sym)
+ }
l.nhashedsyms = len(st.hashed64Syms) + len(st.hashedSyms)
for _, r := range l.objs[goObjStart:] {
loadObjRefs(l, r, arch)
osym := r.Sym(ndef + i)
name := osym.Name(r.Reader)
v := abiToVer(osym.ABI(), r.version)
+ gi := l.LookupOrCreateSym(name, v)
+ r.syms[ndef+i] = gi
if osym.IsLinkname() {
// Check if a linkname reference is allowed.
// Only check references (pull), not definitions (push),
// so push is always allowed.
// Linkname is always a non-package reference.
- checkLinkname(r.unit.Lib.Pkg, name)
+ l.checkLinkname(r.unit.Lib.Pkg, name, gi)
}
- r.syms[ndef+i] = l.LookupOrCreateSym(name, v)
- gi := r.syms[ndef+i]
if osym.Local() {
l.SetAttrLocal(gi, true)
}
// A list of blocked linknames. Some linknames are allowed only
// in specific packages. This maps symbol names to allowed packages.
-// If a name is not in this map, and not with a blocked prefix (see
-// blockedLinknamePrefixes), it is allowed everywhere.
-// If a name is in this map, it is allowed only in listed packages.
+// If a name is not in this map, it is allowed iff the definition
+// has a linkname (push).
+// If a name is in this map, it is allowed only in listed packages,
+// even if it has a linknamed definition.
var blockedLinknames = map[string][]string{
// coroutines
- "runtime.coroexit": nil,
- "runtime.corostart": nil,
"runtime.coroswitch": {"iter"},
"runtime.newcoro": {"iter"},
// weak references
"internal/weak.runtime_registerWeakPointer": {"internal/weak"},
"internal/weak.runtime_makeStrongFromWeak": {"internal/weak"},
- "runtime.getOrAddWeakHandle": nil,
}
-// A list of blocked linkname prefixes (packages).
-var blockedLinknamePrefixes = []string{
- "internal/weak.",
- "internal/concurrent.",
-}
+// check if a linkname reference to symbol s from pkg is allowed
+func (l *Loader) checkLinkname(pkg, name string, s Sym) {
+ if l.flags&FlagCheckLinkname == 0 {
+ return
+ }
-func checkLinkname(pkg, name string) {
error := func() {
- log.Fatalf("linkname or assembly reference of %s is not allowed in package %s", name, pkg)
+ log.Fatalf("%s: invalid reference to %s", pkg, name)
}
pkgs, ok := blockedLinknames[name]
if ok {
}
error()
}
- for _, p := range blockedLinknamePrefixes {
- if strings.HasPrefix(name, p) {
- error()
- }
+ r, li := l.toLocal(s)
+ if r == l.extReader { // referencing external symbol is okay
+ return
+ }
+ if !r.Std() { // For now, only check for symbols defined in std
+ return
+ }
+ if r.unit.Lib.Pkg == pkg { // assembly reference from same package
+ return
+ }
+ osym := r.Sym(li)
+ if osym.IsLinkname() || osym.ABIWrapper() {
+ // Allow if the def has a linkname (push).
+ // ABI wrapper usually wraps an assembly symbol, a linknamed symbol,
+ // or an external symbol, or provide access of a Go symbol to assembly.
+ // For now, allow ABI wrappers.
+ // TODO: check the wrapped symbol?
+ return
}
+ error()
}
// TopLevelSym tests a symbol (by name and kind) to determine whether
}
}
-func TestBlockedLinkname(t *testing.T) {
+func TestCheckLinkname(t *testing.T) {
// Test that code containing blocked linknames does not build.
testenv.MustHaveGoBuild(t)
t.Parallel()
{"push.go", true},
// pull linkname of blocked symbol is not ok
{"coro.go", false},
- {"weak.go", false},
{"coro_var.go", false},
// assembly reference is not ok
{"coro_asm", false},
+ // pull-only linkname is not ok
+ {"coro2.go", false},
+ // legacy bad linkname is ok, for now
+ {"fastrand.go", true},
}
for _, test := range tests {
test := test
t.Parallel()
src := filepath.Join("testdata", "linkname", test.src)
exe := filepath.Join(tmpdir, test.src+".exe")
- cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-checklinkname=1", "-o", exe, src)
out, err := cmd.CombinedOutput()
if test.ok && err != nil {
t.Errorf("build failed unexpectedly: %v:\n%s", err, out)
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Linkname corostart is not allowed, as it doesn't have
+// a linknamed definition.
+
+package main
+
+import _ "unsafe"
+
+//go:linkname corostart runtime.corostart
+func corostart()
+
+func main() {
+ corostart()
+}
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Linkname fastrand is allowed _for now_, as it has a
+// linknamed definition, for legacy reason.
+// NOTE: this may not be allowed in the future. Don't do this!
+
+package main
+
+import _ "unsafe"
+
+//go:linkname fastrand runtime.fastrand
+func fastrand() uint32
+
+func main() {
+ println(fastrand())
+}
+++ /dev/null
-// Copyright 2024 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Linkname generic functions in internal/weak is not
-// allowed; legitimate instantiation is ok.
-
-package main
-
-import (
- "unique"
- "unsafe"
-)
-
-//go:linkname weakMake internal/weak.Make[string]
-func weakMake(string) unsafe.Pointer
-
-func main() {
- h := unique.Make("xxx")
- println(h.Value())
- weakMake("xxx")
-}
< cmp, container/list, container/ring,
internal/cfg, internal/coverage, internal/coverage/rtcov,
internal/coverage/uleb128, internal/coverage/calloc,
- internal/cpu, internal/goarch, internal/godebugs,
+ internal/goarch, internal/godebugs,
internal/goexperiment, internal/goos, internal/byteorder,
internal/goversion, internal/nettrace, internal/platform,
internal/trace/traceviewer/format,
internal/byteorder, internal/goarch, unsafe < internal/chacha8rand;
- unsafe < maps;
+ unsafe < internal/cpu, maps;
# RUNTIME is the core runtime group of packages, all of them very light-weight.
internal/abi,
"go/constant"
"go/token"
. "internal/types/errors"
+ _ "unsafe" // for linkname
)
// An Error describes a type-checking error; it implements the error interface.
_EnableAlias bool
}
+// Linkname for use from srcimporter.
+//go:linkname srcimporter_setUsesCgo
+
func srcimporter_setUsesCgo(conf *Config) {
conf.go115UsesCgo = true
}
// used by the Go standard library.
package cpu
+import _ "unsafe" // for linkname
+
// DebugOptions is set to true by the runtime if the OS supports reading
// GODEBUG early in runtime startup.
// This should not be changed after it is initialized.
_ CacheLinePad
}
+// CPU feature variables are accessed by assembly code in various packages.
+//go:linkname X86
+//go:linkname ARM
+//go:linkname ARM64
+//go:linkname MIPS64X
+//go:linkname PPC64
+//go:linkname S390X
+
// Initialize examines the processor and sets the relevant variables above.
// This is called by the runtime package early in program initialization,
// before normal init functions are run. env is set by runtime if the OS supports
//
//go:linkname Load
//go:linkname Loadp
+//go:linkname LoadAcquintptr
//go:nosplit
//go:noinline
//
//go:linkname Xchg
//go:linkname Xchguintptr
+//go:linkname Xadd
type spinlock struct {
v uint32
//go:linkname Loadint32
//go:linkname Loadint64
//go:linkname Loaduintptr
+//go:linkname LoadAcquintptr
//go:linkname Xadd
//go:linkname Xaddint32
//go:linkname Xaddint64
//go:linkname Storeint32
//go:linkname Storeint64
//go:linkname Storeuintptr
+//go:linkname StoreReluintptr
package atomic
"strconv"
"strings"
"sync"
+ _ "unsafe" // for linkname
)
// TODO: This should be a distinguishable error (ErrMessageTooLarge)
return readMIMEHeader(r, math.MaxInt64, math.MaxInt64)
}
+// readMIMEHeader is accessed from mime/multipart.
+//go:linkname readMIMEHeader
+
// readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size.
// It is called by the mime/multipart package.
func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) {
package os
-import "errors"
+import (
+ "errors"
+ _ "unsafe" // for linkname
+)
+//go:linkname executablePath
var executablePath string // set by ../runtime/os_darwin.go
var initCwd, initCwdErr = Getwd()
package os
-import "syscall"
+import (
+ "syscall"
+ _ "unsafe" // for linkname
+)
+//go:linkname executablePath
var executablePath string // set by sysauxv in ../runtime/os3_solaris.go
var initCwd, initCwdErr = Getwd()
return c
}
-//go:linkname corostart
-
// corostart is the entry func for a new coroutine.
// It runs the coroutine user function f passed to corostart
// and then calls coroexit to remove the extra concurrency.
return nil
}
+// markProfileEmitted is injected to testmain via linkname.
+//go:linkname markProfileEmitted
+
// markProfileEmitted signals the runtime/coverage machinery that
// coverage data output files have already been written out, and there
// is no need to take any additional action at exit time. This
"unsafe"
)
+// processCoverTestDir is injected in testmain.
+//go:linkname processCoverTestDir
+
// processCoverTestDir is called (via a linknamed reference) from
// testmain code when "go test -cover" is in effect. It is not
// intended to be used other than internally by the Go command's
return nil
}
+// snapshot is injected in testmain.
+//go:linkname snapshot
+
// snapshot returns a snapshot of coverage percentage at a moment of
// time within a running test, so as to support the testing.Coverage()
// function. This version doesn't examine coverage meta-data, so the
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+import _ "unsafe"
+
+// used in time and internal/poll
+//go:linkname nanotime
+
+// used in internal/godebug and syscall
+//go:linkname write
+
+// used in internal/runtime/atomic
+//go:linkname goarm
+
+// used by cgo
+//go:linkname cgocall
+//go:linkname _cgo_panic_internal
+//go:linkname cgoAlwaysFalse
+//go:linkname cgoUse
+//go:linkname cgoCheckPointer
+//go:linkname cgoCheckResult
+//go:linkname cgoNoCallback
+//go:linkname gobytes
+//go:linkname gostringn
+//go:linkname throw
+
+// used in plugin
+//go:linkname doInit
+
+// used in math/bits
+//go:linkname overflowError
+//go:linkname divideError
+
+// used in runtime/coverage and in tests
+//go:linkname addExitHook
+
+// used in x/sys/cpu
+//go:linkname getAuxv
+
+// used in tests
+//go:linkname extraMInUse
+//go:linkname getm
+//go:linkname blockevent
+//go:linkname haveHighResSleep
+//go:linkname blockUntilEmptyFinalizerQueue
+//go:linkname lockedOSThread
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build unix
+
+package runtime
+
+import _ "unsafe"
+
+// used in internal/syscall/unix
+//go:linkname fcntl
netpollWaiters atomic.Uint32
)
+// netpollWaiters is accessed in tests
+//go:linkname netpollWaiters
+
//go:linkname poll_runtime_pollServerInit internal/poll.runtime_pollServerInit
func poll_runtime_pollServerInit() {
netpollGenericInit()
return
}
-// This is exported via linkname to assembly in syscall (for Plan9).
+// This is exported via linkname to assembly in syscall (for Plan9) and cgo.
//
//go:linkname gostring
func gostring(p *byte) string {
package runtime
+import _ "unsafe" // for linkname
+
const (
// vdsoArrayMax is the byte-size of a maximally sized array on this architecture.
// See cmd/compile/internal/amd64/galign.go arch.MAXWIDTH initialization.
vdsoGettimeofdaySym uintptr
vdsoClockgettimeSym uintptr
)
+
+// vdsoGettimeofdaySym is accessed from the syscall package.
+//go:linkname vdsoGettimeofdaySym
//go:noescape
func fd_fdstat_set_flags(fd int32, flags fdflags) Errno
+// fd_fdstat_get_flags is accessed from internal/syscall/unix
+//go:linkname fd_fdstat_get_flags
+
func fd_fdstat_get_flags(fd int) (uint32, error) {
var stat fdstat
errno := fd_fdstat_get(int32(fd), unsafe.Pointer(&stat))
return uint32(stat.fdflags), errnoErr(errno)
}
+// fd_fdstat_get_type is accessed from net
+//go:linkname fd_fdstat_get_type
+
func fd_fdstat_get_type(fd int) (uint8, error) {
var stat fdstat
errno := fd_fdstat_get(int32(fd), unsafe.Pointer(&stat))
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin || dragonfly || freebsd || netbsd || openbsd
+
+package syscall
+
+import _ "unsafe"
+
+// used by internal/syscall/unix
+//go:linkname ioctlPtr
+
+// used by x/net/route
+//go:linkname sysctl
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall
+
+import _ "unsafe"
+
+// used by os
+//go:linkname closedir
+//go:linkname readdir_r
+
+// used by internal/poll
+//go:linkname fdopendir
+
+// used by internal/syscall/unix
+//go:linkname unlinkat
+//go:linkname openat
+//go:linkname fstatat
+
+// used by cmd/link
+//go:linkname msync
+//go:linkname fcntl
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build aix || darwin || (openbsd && !mips64) || solaris
+
+package syscall
+
+import _ "unsafe"
+
+// used by internal/poll
+//go:linkname writev
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build openbsd && !mips64
+
+package syscall
+
+import _ "unsafe"
+
+// used by internal/syscall/unix
+//go:linkname unlinkat
+//go:linkname openat
+//go:linkname fstatat
+//go:linkname getentropy
//sys Mlockall(flags int) (err error)
//sys Munlockall() (err error)
+// prlimit is accessed from x/sys/unix.
+//go:linkname prlimit
+
// prlimit changes a resource limit. We use a single definition so that
// we can tell StartProcess to not restore the original NOFILE limit.
// This is unexported but can be called from x/sys/unix.
"fmt"
"internal/goexperiment"
"os"
+ _ "unsafe" // for linkname
)
// cover2 variable stores the current coverage mode and a
snapshotcov func() float64
}
+// registerCover2 is injected in testmain.
+//go:linkname registerCover2
+
// registerCover2 is invoked during "go test -cover" runs by the test harness
// code in _testmain.go; it is used to record a 'tear down' function
// (to be called when the test is complete) and the coverage mode.
}
}
+// testGoCoverDir is used in runtime/coverage tests.
+//go:linkname testGoCoverDir
+
// testGoCoverDir returns the value passed to the -test.gocoverdir
// flag by the Go command, if goexperiment.CoverageRedesign is
// in effect.
"internal/bytealg"
"runtime"
"syscall"
+ _ "unsafe" // for linkname
)
// registerLoadFromEmbeddedTZData is called by the time/tzdata package,
// if it is imported.
+//
+//go:linkname registerLoadFromEmbeddedTZData
func registerLoadFromEmbeddedTZData(f func(string) (string, error)) {
loadFromEmbeddedTZData = f
}