Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
tsuna opened this issue Mar 29, 2016 · 6 comments
Milestone

Comments

@tsuna
Copy link
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
Copy link
Contributor

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
Copy link
Member

minux commented Mar 30, 2016 via email

@randall77
Copy link
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.

@dvyukov
Copy link
Member

dvyukov commented Apr 18, 2017

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

@dvyukov dvyukov closed this as completed Apr 18, 2017
@bigfg
Copy link

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
Copy link
Contributor

@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.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants