]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: only include the version info and export data in ABI hash
authorMichael Hudson-Doyle <michael.hudson@canonical.com>
Wed, 12 Apr 2017 04:08:46 +0000 (16:08 +1200)
committerMichael Hudson-Doyle <michael.hudson@canonical.com>
Mon, 17 Apr 2017 22:02:01 +0000 (22:02 +0000)
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 <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
misc/cgo/testshared/shared_test.go
src/cmd/link/internal/ld/lib.go

index a7cec9b2e8aa001b30abb73578f7c11bc7088ead..9e682a2fb5931144c889da4b31fb090589cf0fdd 100644 (file)
@@ -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")
 }
index 0319a3edfd2f7f6d1ceb9d680e2e3ae89ab84f77..383e16e4f2a8d85a20506ddc00c25de33f0f7081 100644 (file)
@@ -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))
 }