summaryrefslogtreecommitdiffhomepage
path: root/tools/checklinkname/known.go
diff options
context:
space:
mode:
authorMichael Pratt <mpratt@google.com>2021-07-30 13:39:18 -0700
committergVisor bot <gvisor-bot@google.com>2021-07-30 13:42:15 -0700
commit62ea5c0a2212b9827f093551fc3da166facb9f0b (patch)
treee26283ab57a11a34202cc731da4270c53c418951 /tools/checklinkname/known.go
parent095b0d8348531b96f1b40885c00d6cd7f07ecf80 (diff)
checklinkname: rudimentary type-checking of linkname directives
This CL introduces a 'checklinkname' analyzer, which provides rudimentary type-checking that verifies that function signatures on the local and remote sides of //go:linkname directives match expected values. If the Go standard library changes the definitions of any of these function, checklinkname will flag the change as a finding, providing an error informing the gVisor team to adapt to the upstream changes. This allows us to eliminate the majority of gVisor's forward-looking negative build tags, as we can catch mismatches in testing [1]. The remaining forward-looking negative build tags are covering shared struct definitions, which I hope to add to checklinkname in a future CL. [1] Of course, semantics/requirements can change without the signature changing, so we still must be careful, but this covers the common case. PiperOrigin-RevId: 387873847
Diffstat (limited to 'tools/checklinkname/known.go')
-rw-r--r--tools/checklinkname/known.go110
1 files changed, 110 insertions, 0 deletions
diff --git a/tools/checklinkname/known.go b/tools/checklinkname/known.go
new file mode 100644
index 000000000..54e5155fc
--- /dev/null
+++ b/tools/checklinkname/known.go
@@ -0,0 +1,110 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package checklinkname
+
+// knownLinknames is the set of the symbols for which we can do a rudimentary
+// type-check on.
+//
+// When analyzing the remote package (e.g., runtime), we verify the symbol
+// signature matches 'remote'. When analyzing local packages with //go:linkname
+// directives, we verify the symbol signature matches 'local'.
+//
+// Usually these are identical, but may differ slightly if equivalent
+// replacement types are used in the local packages, such as a copy of a struct
+// or uintptr instead of a pointer type.
+//
+// NOTE: It is the responsibility of the developer to verify the safety of the
+// signatures used here! This analyzer only checks that types match this map;
+// it does not verify compatibility of the entries themselves.
+//
+// //go:linkname directives with no corresponding entry here will trigger a
+// finding.
+//
+// We preform only rudimentary string-based type-checking due to limitations in
+// the analysis framework. Ideally, from the local package we'd lookup the
+// remote symbol's types.Object and perform robust type-checking.
+// Unfortunately, remote symbols are typically loaded from the remote package's
+// gcexportdata. Since //go:linkname targets are usually not exported symbols,
+// they are no included in gcexportdata and we cannot load their types.Object.
+//
+// TODO(b/165820485): Add option to specific per-version signatures.
+var knownLinknames = map[string]map[string]linknameSignatures{
+ "runtime": map[string]linknameSignatures{
+ "entersyscall": linknameSignatures{
+ local: "func()",
+ },
+ "entersyscallblock": linknameSignatures{
+ local: "func()",
+ },
+ "exitsyscall": linknameSignatures{
+ local: "func()",
+ },
+ "fastrand": linknameSignatures{
+ local: "func() uint32",
+ },
+ "gopark": linknameSignatures{
+ // TODO(b/165820485): add verification of waitReason
+ // size and reason and traceEv values.
+ local: "func(unlockf func(uintptr, unsafe.Pointer) bool, lock unsafe.Pointer, reason uint8, traceEv byte, traceskip int)",
+ remote: "func(unlockf func(*runtime.g, unsafe.Pointer) bool, lock unsafe.Pointer, reason runtime.waitReason, traceEv byte, traceskip int)",
+ },
+ "goready": linknameSignatures{
+ local: "func(gp uintptr, traceskip int)",
+ remote: "func(gp *runtime.g, traceskip int)",
+ },
+ "goyield": linknameSignatures{
+ local: "func()",
+ },
+ "memmove": linknameSignatures{
+ local: "func(to unsafe.Pointer, from unsafe.Pointer, n uintptr)",
+ },
+ "throw": linknameSignatures{
+ local: "func(s string)",
+ },
+ },
+ "sync": map[string]linknameSignatures{
+ "runtime_canSpin": linknameSignatures{
+ local: "func(i int) bool",
+ },
+ "runtime_doSpin": linknameSignatures{
+ local: "func()",
+ },
+ "runtime_Semacquire": linknameSignatures{
+ // The only difference here is the parameter names. We
+ // can't just change our local use to match remote, as
+ // the stdlib runtime and sync packages also disagree
+ // on the name, and the analyzer checks that use as
+ // well.
+ local: "func(addr *uint32)",
+ remote: "func(s *uint32)",
+ },
+ "runtime_Semrelease": linknameSignatures{
+ // See above.
+ local: "func(addr *uint32, handoff bool, skipframes int)",
+ remote: "func(s *uint32, handoff bool, skipframes int)",
+ },
+ },
+ "syscall": map[string]linknameSignatures{
+ "runtime_BeforeFork": linknameSignatures{
+ local: "func()",
+ },
+ "runtime_AfterFork": linknameSignatures{
+ local: "func()",
+ },
+ "runtime_AfterForkInChild": linknameSignatures{
+ local: "func()",
+ },
+ },
+}