Skip to content

crypto/sha256: amd64 block assembly smashes frame pointer register #63508

@prattmic

Description

@prattmic

What version of Go are you using (go version)?

$ go version
go version devel go1.22-f09750e8ef Wed Oct 11 13:53:10 2023 -0400 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

linux-amd64

go env Output
$ go env
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/usr/local/google/home/mpratt/.cache/go-build'
GOENV='/usr/local/google/home/mpratt/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/usr/local/google/home/mpratt/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/usr/local/google/home/mpratt/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/google/home/mpratt/src/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/google/home/mpratt/src/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='devel go1.22-f09750e8ef Wed Oct 11 13:53:10 2023 -0400'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/usr/local/google/home/mpratt/src/go/src/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1220716334=/tmp/go-build -gno-record-gcc-switches'

What did you do?

Profile the crypto/sha256 benchmarks, using both the built-in pprof profiling and using Linux perf:

$ go test -c crypto/sha256
$ ./sha256.test -test.run=^$ -test.bench=. -test.cpuprofile=/tmp/cpu.pprof
$ perf record -g ./sha256.test -test.run=^$ -test.bench=.

Then open the profiles with pprof and compare the flame graphs:

$ pprof -http=localhost:8080 /tmp/cpu.pprof
$ pprof -http=localhost:8080 perf.data

What did you expect to see?

Approximately the same profile.

What did you see instead?

The pprof profile looks normal, as expected:

image

The perf profile has broken call stacks, displaying sha256.block by itself, rather than as a callee of Write.

image


pprof profiling uses the runtime's internal knowledge of frame sizes to find the caller, so it works fine. The Linux kernel doesn't know about Go's metadata and instead typically uses frame pointers (BP) to find callees.

For some reason, the amd64 implementation of block overwrites BP, which I presume is what is confusing Linux.

I admittedly don't understand everything going on in that function (wow, it is complicated!), but writing to BP doesn't seem to be necessary? The function doesn't seem to actually adjust the stack or create new frames? It looks like BP might just be getting used as a scratch register?

It looks like this has existed since the original https://go.dev/cl/28460043.

cc @FiloSottile @rolandshoemaker @felixge @4a6f656c

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.OS-LinuxPerformance

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions