i := int(s.SymIdx) + r.ndef + r.nhashed64def + r.nhasheddef
return r.syms[i]
case goobj.PkgIdxBuiltin:
- return l.builtinSyms[s.SymIdx]
+ if bi := l.builtinSyms[s.SymIdx]; bi != 0 {
+ return bi
+ }
+ l.reportMissingBuiltin(int(s.SymIdx), r.unit.Lib.Pkg)
+ return 0
case goobj.PkgIdxSelf:
rr = r
default:
return l.toGlobal(rr, s.SymIdx)
}
+// reportMissingBuiltin issues an error in the case where we have a
+// relocation against a runtime builtin whose definition is not found
+// when the runtime package is built. The canonical example is
+// "runtime.racefuncenter" -- currently if you do something like
+//
+// go build -gcflags=-race myprogram.go
+//
+// the compiler will insert calls to the builtin runtime.racefuncenter,
+// but the version of the runtime used for linkage won't actually contain
+// definitions of that symbol. See issue #42396 for details.
+//
+// As currently implemented, this is a fatal error. This has drawbacks
+// in that if there are multiple missing builtins, the error will only
+// cite the first one. On the plus side, terminating the link here has
+// advantages in that we won't run the risk of panics or crashes later
+// on in the linker due to R_CALL relocations with 0-valued target
+// symbols.
+func (l *Loader) reportMissingBuiltin(bsym int, reflib string) {
+ bname, _ := goobj.BuiltinName(bsym)
+ log.Fatalf("reference to undefined builtin %q from package %q",
+ bname, reflib)
+}
+
// Look up a symbol by name, return global index, or 0 if not found.
// This is more like Syms.ROLookup than Lookup -- it doesn't create
// new symbol.
import (
"bufio"
"bytes"
+ "cmd/internal/sys"
"debug/macho"
"internal/testenv"
"io/ioutil"
t.Errorf("binary too big: got %d, want < %d", got, want)
}
}
+
+const testIssue42396src = `
+package main
+
+//go:noinline
+//go:nosplit
+func callee(x int) {
+}
+
+func main() {
+ callee(9)
+}
+`
+
+func TestIssue42396(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if !sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
+ t.Skip("no race detector support")
+ }
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestIssue42396")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "main.go")
+ err = ioutil.WriteFile(src, []byte(testIssue42396src), 0666)
+ if err != nil {
+ t.Fatalf("failed to write source file: %v", err)
+ }
+ exe := filepath.Join(tmpdir, "main.exe")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Fatalf("build unexpectedly succeeded")
+ }
+
+ // Check to make sure that we see a reasonable error message
+ // and not a panic.
+ if strings.Contains(string(out), "panic:") {
+ t.Fatalf("build should not fail with panic:\n%s", out)
+ }
+ const want = "reference to undefined builtin"
+ if !strings.Contains(string(out), want) {
+ t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
+ }
+}