mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/link: type symbol name mangling for plugins
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>
This commit is contained in:
parent
be272ec071
commit
24e4a128c9
5 changed files with 101 additions and 27 deletions
15
misc/cgo/testplugin/src/issue19529/plugin.go
Normal file
15
misc/cgo/testplugin/src/issue19529/plugin.go
Normal file
|
|
@ -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() {}
|
||||||
|
|
@ -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" -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
|
GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue19418 src/issue19418/main.go
|
||||||
./issue19418
|
./issue19418
|
||||||
|
|
||||||
|
# Test for issue 19529
|
||||||
|
GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o plugin.so src/issue19529/plugin.go
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"cmd/internal/sys"
|
"cmd/internal/sys"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"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
|
// If package versioning is required, generate a hash of the
|
||||||
// the packages used in the link.
|
// 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 {
|
for _, lib := range ctxt.Library {
|
||||||
if lib.Shlib == "" {
|
if lib.Shlib == "" {
|
||||||
genhash(ctxt, lib)
|
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.
|
* look for the next file in an archive.
|
||||||
* adapted from libmach.
|
* adapted from libmach.
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ import (
|
||||||
"cmd/internal/dwarf"
|
"cmd/internal/dwarf"
|
||||||
"cmd/internal/objabi"
|
"cmd/internal/objabi"
|
||||||
"cmd/internal/sys"
|
"cmd/internal/sys"
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/base64"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -513,30 +511,6 @@ func (r *objReader) readSymName() string {
|
||||||
r.readFull(r.rdBuf[:n])
|
r.readFull(r.rdBuf[:n])
|
||||||
}
|
}
|
||||||
r.rdBuf = adjName[:0] // in case 2*n wasn't enough
|
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
|
return s
|
||||||
}
|
}
|
||||||
adjName = append(adjName, origName[:i]...)
|
adjName = append(adjName, origName[:i]...)
|
||||||
|
|
|
||||||
|
|
@ -82,3 +82,25 @@ func (syms *Symbols) IncVersion() int {
|
||||||
syms.hash = append(syms.hash, make(map[string]*Symbol))
|
syms.hash = append(syms.hash, make(map[string]*Symbol))
|
||||||
return len(syms.hash) - 1
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue