From: Michael Hudson-Doyle Date: Wed, 12 Apr 2017 04:08:46 +0000 (+1200) Subject: cmd/link: only include the version info and export data in ABI hash X-Git-Tag: go1.9beta1~657 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=533ed967c6e842b4f9b9b503d20b3af698a4e022;p=gostls13.git cmd/link: only include the version info and export data in ABI hash Previously the "ABI hash" for a package (used to determine if a loaded shared library has the ABI expected by its loader) was the hash of the entire __.PKGDEF file. But that means it depends on the build ID generated by the go tool for the package, which means that if a file is added (even a .c or .h file!) to the package, the ABI changes, perhaps uncessarily. Fixes #19920 Change-Id: If919481e1a03afb350c8a9c7a0666bb90ee90270 Reviewed-on: https://go-review.googlesource.com/40401 Run-TryBot: Michael Hudson-Doyle TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index a7cec9b2e8..9e682a2fb5 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -763,6 +763,13 @@ func appendFile(path, content string) { } } +func writeFile(path, content string) { + err := ioutil.WriteFile(path, []byte(content), 0644) + if err != nil { + log.Fatalf("ioutil.WriteFile failed: %v", err) + } +} + func TestABIChecking(t *testing.T) { goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") goCmd(t, "install", "-linkshared", "exe") @@ -801,9 +808,10 @@ func TestABIChecking(t *testing.T) { run(t, "rebuilt exe", "./bin/exe") // If we make a change which does not break ABI (such as adding an unexported - // function) and rebuild libdepBase.so, exe still works. + // function) and rebuild libdepBase.so, exe still works, even if new function + // is in a file by itself. resetFileStamps() - appendFile("src/depBase/dep.go", "func noABIBreak() {}\n") + writeFile("src/depBase/dep2.go", "package depBase\nfunc noABIBreak() {}\n") goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") run(t, "after non-ABI breaking change", "./bin/exe") } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 0319a3edfd..383e16e4f2 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -720,10 +720,35 @@ func genhash(ctxt *Link, lib *Library) { } h := sha1.New() - if _, err := io.CopyN(h, f, atolwhex(arhdr.size)); err != nil { - Errorf(nil, "bad read of %s for hash generation: %v", lib.File, err) + + // To compute the hash of a package, we hash the first line of + // __.PKGDEF (which contains the toolchain version and any + // GOEXPERIMENT flags) and the export data (which is between + // the first two occurences of "\n$$"). + + pkgDefBytes := make([]byte, atolwhex(arhdr.size)) + _, err = io.ReadFull(f, pkgDefBytes) + if err != nil { + Errorf(nil, "%s: error reading package data: %v", lib.File, err) + return + } + firstEOL := bytes.Index(pkgDefBytes, []byte("\n")) + if firstEOL < 0 { + Errorf(nil, "cannot parse package data of %s for hash generation, no newline found", lib.File) + return + } + firstDoubleDollar := bytes.Index(pkgDefBytes, []byte("\n$$")) + if firstDoubleDollar < 0 { + Errorf(nil, "cannot parse package data of %s for hash generation, no \\n$$ found", lib.File) + return + } + secondDoubleDollar := bytes.Index(pkgDefBytes[firstDoubleDollar+1:], []byte("\n$$")) + if secondDoubleDollar < 0 { + Errorf(nil, "cannot parse package data of %s for hash generation, only one \\n$$ found", lib.File) return } + h.Write(pkgDefBytes[0:firstEOL]) + h.Write(pkgDefBytes[firstDoubleDollar : firstDoubleDollar+secondDoubleDollar]) lib.hash = hex.EncodeToString(h.Sum(nil)) }