cmd/compile: "missing function body" error when using the //go:linkname compiler directive #15006

Closed
tsuna opened this Issue Mar 29, 2016 · 6 comments

Comments

Projects
None yet
7 participants
@tsuna
Contributor

tsuna commented Mar 29, 2016

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

go version go1.6 darwin/amd64
go version devel +aa3650f Wed Mar 9 09:13:43 2016 +0000 darwin/amd64

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

Mac OS X 10.9.5 on Intel Core i7-3615QM

GOARCH="amd64"
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/tsuna/go"
GOROOT="/usr/local/Cellar/go/1.6/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.6/libexec/pkg/tool/darwin_amd64"
GO15VENDOREXPERIMENT="1"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
CXX="clang++"
CGO_ENABLED="1"
What did you do?

The Go manual on Compiler Directives says:

The //go:linkname directive instructs the compiler to use “importpath.name” as the object file symbol name for the variable or function declared as “localname” in the source code.

For example let's say (hypothetically) that I wanted to call the strhash function defined in the runtime package, I could do:

package key

import "unsafe"

//go:linkname strhash runtime.strhash
func strhash(a unsafe.Pointer, h uintptr) uintptr

func hash(s string) uintptr {
    return strhash(unsafe.Pointer(&s), 0)
}
What did you expect to see?

The code above should build with no special ceremony.

What did you see instead?

The code doesn't build for a silly reason:

strhash.go:6: missing function body for "strhash"

This comes from the fact that in cmd/compile/internal/gc/pgen.go we do:

    if fn.Nbody == nil {
        if pure_go != 0 || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
            Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name)
            goto ret
        }

So in order to pass this check, we need to not have pure_go, which comes from the -complete flag in cmd/compile/internal/gc/lex.go:

    obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go)

which is passed from cmd/go/build.go under the following circumstances:

    // If we're giving the compiler the entire package (no C etc files), tell it that,
    // so that it can give good error messages about forward declarations.
    // Exceptions: a few standard packages have forward declarations for
    // pieces supplied behind-the-scenes by package runtime.
    extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
    if p.Standard {
        switch p.ImportPath {
        case "bytes", "net", "os", "runtime/pprof", "sync", "time":
            extFiles++
        }
    }
    if extFiles == 0 {
        gcargs = append(gcargs, "-complete")
    }

So one workaround to not be -complete is to include an empty .s file in the package using the //go:linkname directive, as this will make len(p.SFiles) be 1, which in turn will make extFiles non-zero, so that the -complete flag won't get passed and we can pass the check above, and then the code builds and runs as expected. Phew!

@ianlancetaylor

This comment has been minimized.

Show comment
Hide comment
@ianlancetaylor

ianlancetaylor Mar 29, 2016

Contributor

How do you suggest we fix this? We certainly do want to pass -complete in the normal case.

Contributor

ianlancetaylor commented Mar 29, 2016

How do you suggest we fix this? We certainly do want to pass -complete in the normal case.

@ianlancetaylor ianlancetaylor added this to the Unplanned milestone Mar 29, 2016

@minux

This comment has been minimized.

Show comment
Hide comment
@minux

minux Mar 30, 2016

Member
Member

minux commented Mar 30, 2016

@randall77

This comment has been minimized.

Show comment
Hide comment
@randall77

randall77 Mar 30, 2016

Contributor

I agree with Minux. If you're looking at a Go package to import, you might want to know if it does any unsafe trickery. Currently you have to grep for an import of unsafe and look for non-.go files. If we got rid of the requirement for the empty .s file, then you'd have to grep for //go:linkname also.

Contributor

randall77 commented Mar 30, 2016

I agree with Minux. If you're looking at a Go package to import, you might want to know if it does any unsafe trickery. Currently you have to grep for an import of unsafe and look for non-.go files. If we got rid of the requirement for the empty .s file, then you'd have to grep for //go:linkname also.

@tsuna tsuna referenced this issue in aristanetworks/goarista Jan 4, 2017

Closed

How does the 'monotime' package work? #13

@dvyukov

This comment has been minimized.

Show comment
Hide comment
@dvyukov

dvyukov Apr 18, 2017

Member

It seems the agreement is to add .s file. Closing this.

Member

dvyukov commented Apr 18, 2017

It seems the agreement is to add .s file. Closing this.

@dvyukov dvyukov closed this Apr 18, 2017

@bigfg

This comment has been minimized.

Show comment
Hide comment
@bigfg

bigfg Jan 11, 2018

actually, I try to fix this with:
flag.BoolVar(&completeFlag, "complete", true, "compiling complete package")

But it doesn't seem to work.

bigfg commented Jan 11, 2018

actually, I try to fix this with:
flag.BoolVar(&completeFlag, "complete", true, "compiling complete package")

But it doesn't seem to work.

@davecheney

This comment has been minimized.

Show comment
Hide comment
@davecheney

davecheney Jan 11, 2018

Contributor

@bigfg this issue is closed, please open a new issue.

Contributor

davecheney commented Jan 11, 2018

@bigfg this issue is closed, please open a new issue.

@golang golang locked as resolved and limited conversation to collaborators Jan 11, 2018

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.