From dde20209b8cc69df89ab70e6a0830ff90176411a Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 29 Aug 2023 06:51:54 -0700 Subject: [PATCH] runtime: correct linux-arm64 vdso hash codes Also add a test that the VDSO is actually working. Fixes #62309 Change-Id: Ia846b36dfc21716f1653bdf2671485a4cf4a7bd1 Reviewed-on: https://go-review.googlesource.com/c/go/+/523955 Auto-Submit: Ian Lance Taylor Reviewed-by: Ian Lance Taylor Reviewed-by: Austin Clements LUCI-TryBot-Result: Go LUCI --- src/runtime/vdso_linux_arm64.go | 2 +- src/runtime/vdso_test.go | 132 ++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/runtime/vdso_test.go diff --git a/src/runtime/vdso_linux_arm64.go b/src/runtime/vdso_linux_arm64.go index 2f003cd645..f5959525af 100644 --- a/src/runtime/vdso_linux_arm64.go +++ b/src/runtime/vdso_linux_arm64.go @@ -14,7 +14,7 @@ const ( var vdsoLinuxVersion = vdsoVersionKey{"LINUX_2.6.39", 0x75fcb89} var vdsoSymbolKeys = []vdsoSymbolKey{ - {"__kernel_clock_gettime", 0xd35ec75, 0x6e43a318, &vdsoClockgettimeSym}, + {"__kernel_clock_gettime", 0xb0cd725, 0xdfa941fd, &vdsoClockgettimeSym}, } // initialize to fall back to syscall diff --git a/src/runtime/vdso_test.go b/src/runtime/vdso_test.go new file mode 100644 index 0000000000..61f651614b --- /dev/null +++ b/src/runtime/vdso_test.go @@ -0,0 +1,132 @@ +// Copyright 2023 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 (freebsd && (386 || amd64 || arm || arm64 || riscv64)) || (linux && (386 || amd64 || arm || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x)) + +package runtime_test + +import ( + "bytes" + "internal/testenv" + "os" + "os/exec" + "path/filepath" + "testing" + "time" +) + +// TestUsingVDSO tests that we are actually using the VDSO to fetch +// the time. +func TestUsingVDSO(t *testing.T) { + const calls = 100 + + if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { + // Fetch the time a lot. + var total int64 + for i := 0; i < calls; i++ { + total += time.Now().UnixNano() + } + os.Exit(0) + } + + t.Parallel() + + // Look for strace in /bin or /usr/bin. Don't assume that some + // strace on PATH is the one that we want. + strace := "/bin/strace" + if _, err := os.Stat(strace); err != nil { + strace = "/usr/bin/strace" + if _, err := os.Stat(strace); err != nil { + t.Skipf("skipping test because strace not found: %v", err) + } + } + + exe, err := os.Executable() + if err != nil { + t.Skipf("skipping because Executable failed: %v", err) + } + + t.Logf("GO_WANT_HELPER_PROCESS=1 %s -f -e clock_gettime %s -test.run=TestUsingVDSO", strace, exe) + cmd := testenv.Command(t, strace, "-f", "-e", "clock_gettime", exe, "-test.run=TestUsingVDSO") + cmd = testenv.CleanCmdEnv(cmd) + cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1") + out, err := cmd.CombinedOutput() + if len(out) > 0 { + t.Logf("%s", out) + } + if err != nil { + t.Fatal(err) + } + + if got := bytes.Count(out, []byte("gettime")); got >= calls { + t.Logf("found %d gettime calls, want < %d", got, calls) + + // Try to double-check that a C program uses the VDSO. + tempdir := t.TempDir() + cfn := filepath.Join(tempdir, "time.c") + cexe := filepath.Join(tempdir, "time") + if err := os.WriteFile(cfn, []byte(vdsoCProgram), 0o644); err != nil { + t.Fatal(err) + } + cc := os.Getenv("CC") + if cc == "" { + cc, err = exec.LookPath("gcc") + if err != nil { + cc, err = exec.LookPath("clang") + if err != nil { + t.Skip("can't verify VDSO status, no C compiler") + } + } + } + + t.Logf("%s -o %s %s", cc, cexe, cfn) + cmd = testenv.Command(t, cc, "-o", cexe, cfn) + cmd = testenv.CleanCmdEnv(cmd) + out, err = cmd.CombinedOutput() + if len(out) > 0 { + t.Logf("%s", out) + } + if err != nil { + t.Skipf("can't verify VDSO status, C compiled failed: %v", err) + } + + t.Logf("%s -f -e clock_gettime %s", strace, cexe) + cmd = testenv.Command(t, strace, "-f", "-e", "clock_gettime", cexe) + cmd = testenv.CleanCmdEnv(cmd) + out, err = cmd.CombinedOutput() + if len(out) > 0 { + t.Logf("%s", out) + } + if err != nil { + t.Skipf("can't verify VDSO status, C program failed: %v", err) + } + + if cgot := bytes.Count(out, []byte("gettime")); cgot >= 100 { + t.Logf("found %d gettime calls, want < %d", cgot, 100) + t.Log("C program does not use VDSO either") + return + } + + // The Go program used the system call but the C + // program did not. This is a VDSO failure for Go. + t.Errorf("did not use VDSO system call") + } +} + +const vdsoCProgram = ` +#include +#include + +int main() { + int i; + time_t tot; + for (i = 0; i < 100; i++) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + tot += ts.tv_nsec; + } + printf("%d\n", (int)(tot)); + return 0; +} +` -- 2.50.0