]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: type symbol name mangling for plugins
authorDavid Crawshaw <crawshaw@golang.org>
Sat, 30 Sep 2017 12:36:34 +0000 (12:36 +0000)
committerDavid Crawshaw <crawshaw@golang.org>
Thu, 5 Oct 2017 09:51:31 +0000 (09:51 +0000)
Moves type symbol name mangling out of the object reader
and into a separate pass. Requires some care, as changing
the name of a type may require dealing with duplicate
symbols for the first time.

Disables DWARF for both plugins and programs that use plugin.Open,
because type manging is currently incompatible with the go.info.*
symbol generator in cmd/link. (It relies on the symbol names to
find type information.) A future fix for this would be moving the
go.info.* generation into the compiler, with the logic we use
for generating the type.* symbols.

Fixes #19529

Change-Id: I75615f8bdda86ff9e767e536d9aa36e15c194098
Reviewed-on: https://go-review.googlesource.com/67312
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
misc/cgo/testplugin/src/issue19529/plugin.go [new file with mode: 0644]
misc/cgo/testplugin/test.bash
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/objfile.go
src/cmd/link/internal/ld/symbols.go

diff --git a/misc/cgo/testplugin/src/issue19529/plugin.go b/misc/cgo/testplugin/src/issue19529/plugin.go
new file mode 100644 (file)
index 0000000..ad2df6c
--- /dev/null
@@ -0,0 +1,15 @@
+package main
+
+import (
+       "reflect"
+)
+
+type Foo struct {
+       Bar string `json:"Bar@baz,omitempty"`
+}
+
+func F() {
+       println(reflect.TypeOf(Foo{}).Field(0).Tag)
+}
+
+func main() {}
index ae3368b45f60aad7058b61b48aaff2be4068d303..6c13aa5fd79e8fb166408f5ee135d2c4acd1de2b 100755 (executable)
@@ -71,3 +71,6 @@ GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue18584 src/issue18584/main.
 GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin "-ldflags=-X main.Val=linkstr" -o plugin.so src/issue19418/plugin.go
 GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue19418 src/issue19418/main.go
 ./issue19418
+
+# Test for issue 19529
+GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o plugin.so src/issue19529/plugin.go
index 42498cbc9beb0a90925be041181d1985b7573568..1cedb44a384ce764c20d0150b7808f9542fc5aa2 100644 (file)
@@ -38,6 +38,7 @@ import (
        "cmd/internal/sys"
        "crypto/sha1"
        "debug/elf"
+       "encoding/base64"
        "encoding/binary"
        "encoding/hex"
        "fmt"
@@ -583,9 +584,30 @@ func (ctxt *Link) loadlib() {
                }
        }
 
+       // If type. symbols are visible in the symbol table, rename them
+       // using a SHA-1 prefix. This reduces binary size (the full
+       // string of a type symbol can be multiple kilobytes) and removes
+       // characters that upset external linkers.
+       //
+       // Keep the type.. prefix, which parts of the linker (like the
+       // DWARF generator) know means the symbol is not decodable.
+       //
+       // Leave type.runtime. symbols alone, because other parts of
+       // the linker manipulates them, and also symbols whose names
+       // would not be shortened by this process.
+       if typeSymbolMangling(ctxt.Syms) {
+               *FlagW = true // disable DWARF generation
+               for _, s := range ctxt.Syms.Allsym {
+                       newName := typeSymbolMangle(ctxt.Syms, s.Name)
+                       if newName != s.Name {
+                               ctxt.Syms.Rename(s.Name, newName, int(s.Version))
+                       }
+               }
+       }
+
        // If package versioning is required, generate a hash of the
        // the packages used in the link.
-       if Buildmode == BuildmodeShared || Buildmode == BuildmodePlugin || ctxt.Syms.ROLookup("plugin.Open", 0) != nil {
+       if Buildmode == BuildmodeShared || Buildmode == BuildmodePlugin || ctxt.CanUsePlugins() {
                for _, lib := range ctxt.Library {
                        if lib.Shlib == "" {
                                genhash(ctxt, lib)
@@ -642,6 +664,44 @@ func (ctxt *Link) loadlib() {
        }
 }
 
+// typeSymbolMangling reports whether the linker should shorten the
+// names of symbols that represent Go types.
+//
+// As the names of these symbols are derived from the string of
+// the type, they can run to many kilobytes long. So we shorten
+// them using a SHA-1 when the name appears in the final binary.
+//
+// These are the symbols that begin with the prefix 'type.' and
+// contain run-time type information used by the runtime and reflect
+// packages. All Go binaries contain these symbols, but only only
+// those programs loaded dynamically in multiple parts need these
+// symbols to have entries in the symbol table.
+func typeSymbolMangling(syms *Symbols) bool {
+       return Buildmode == BuildmodeShared || *FlagLinkshared || Buildmode == BuildmodePlugin || syms.ROLookup("plugin.Open", 0) != nil
+}
+
+// typeSymbolMangle mangles the given symbol name into something shorter.
+func typeSymbolMangle(syms *Symbols, name string) string {
+       if !typeSymbolMangling(syms) {
+               return name
+       }
+       if !strings.HasPrefix(name, "type.") {
+               return name
+       }
+       if strings.HasPrefix(name, "type.runtime.") {
+               return name
+       }
+       if len(name) <= 14 && !strings.Contains(name, "@") { // Issue 19529
+               return name
+       }
+       hash := sha1.Sum([]byte(name))
+       prefix := "type."
+       if name[5] == '.' {
+               prefix = "type.."
+       }
+       return prefix + base64.StdEncoding.EncodeToString(hash[:6])
+}
+
 /*
  * look for the next file in an archive.
  * adapted from libmach.
index a7ca2a61ec80b424013e86f86d0a17e255a6ac2e..c3bf281a6c2267661b779c1894bea4962010f6d9 100644 (file)
@@ -13,8 +13,6 @@ import (
        "cmd/internal/dwarf"
        "cmd/internal/objabi"
        "cmd/internal/sys"
-       "crypto/sha1"
-       "encoding/base64"
        "io"
        "log"
        "strconv"
@@ -513,30 +511,6 @@ func (r *objReader) readSymName() string {
                                r.readFull(r.rdBuf[:n])
                        }
                        r.rdBuf = adjName[:0] // in case 2*n wasn't enough
-
-                       if Buildmode == BuildmodeShared || *FlagLinkshared {
-                               // These types are included in the symbol
-                               // table when dynamically linking. To keep
-                               // binary size down, we replace the names
-                               // with SHA-1 prefixes.
-                               //
-                               // Keep the type.. prefix, which parts of the
-                               // linker (like the DWARF generator) know means
-                               // the symbol is not decodable.
-                               //
-                               // Leave type.runtime. symbols alone, because
-                               // other parts of the linker manipulates them,
-                               // and also symbols whose names would not be
-                               // shortened by this process.
-                               if len(s) > 14 && strings.HasPrefix(s, "type.") && !strings.HasPrefix(s, "type.runtime.") {
-                                       hash := sha1.Sum([]byte(s))
-                                       prefix := "type."
-                                       if s[5] == '.' {
-                                               prefix = "type.."
-                                       }
-                                       s = prefix + base64.StdEncoding.EncodeToString(hash[:6])
-                               }
-                       }
                        return s
                }
                adjName = append(adjName, origName[:i]...)
index 154507ddd75d4a68dea28d4a67aaedfd11c4c686..8708fc8aaefc2851823fdd9a039701ec7f44d1e5 100644 (file)
@@ -82,3 +82,25 @@ func (syms *Symbols) IncVersion() int {
        syms.hash = append(syms.hash, make(map[string]*Symbol))
        return len(syms.hash) - 1
 }
+
+// Rename renames a symbol.
+func (syms *Symbols) Rename(old, new string, v int) {
+       s := syms.hash[v][old]
+       s.Name = new
+       if s.Extname == old {
+               s.Extname = new
+       }
+       delete(syms.hash[v], old)
+
+       dup := syms.hash[v][new]
+       if dup == nil {
+               syms.hash[v][new] = s
+       } else {
+               if s.Type == 0 {
+                       *s = *dup
+               } else if dup.Type == 0 {
+                       *dup = *s
+                       syms.hash[v][new] = s
+               }
+       }
+}