cmd/internal/ld: do not depend on local symbols to read a type's gcdata

We already read the address of a gcmask/gcprog out of the type data, but I
didn't know how many bytes to read. But it turns out that it's easy to
calculate, so change to do that. This means that we no longer depend on the
local symbols being present, allowing me to strip the shared libraries for
distribution and make them a lot smaller.

As a bonus, this makes LSym another 24 bytes smaller, down to 296 bytes now.

Change-Id: I379d359e28d63afae6753efd23efdf1fbb716992
Reviewed-on: https://go-review.googlesource.com/10377
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Michael Hudson-Doyle 2015-05-25 16:13:50 +12:00 committed by Ian Lance Taylor
parent bcc1870fcf
commit c949cff6a7
6 changed files with 64 additions and 53 deletions

View file

@ -2,6 +2,12 @@ package dep
var V int = 1 var V int = 1
var HasMask []string = []string{"hi"}
type HasProg struct {
array [1024]*byte
}
func F() int { func F() int {
return V return V
} }

View file

@ -4,6 +4,8 @@ import "dep"
var W int = 1 var W int = 1
var hasProg dep.HasProg
func G() int { func G() int {
return dep.F() + 1 return dep.F() + 1
} }

View file

@ -1104,7 +1104,7 @@ func (p *GCProg) AddSym(s *LSym) {
// Copy program. // Copy program.
prog := decodetype_gcprog(typ) prog := decodetype_gcprog(typ)
p.w.ZeroUntil(s.Value / ptrsize) p.w.ZeroUntil(s.Value / ptrsize)
p.w.Append(prog.P[4:prog.Size], nptr) p.w.Append(prog[4:], nptr)
} }
func growdatsize(datsizep *int64, s *LSym) { func growdatsize(datsizep *int64, s *LSym) {

View file

@ -4,7 +4,10 @@
package ld package ld
import "cmd/internal/obj" import (
"cmd/internal/obj"
"debug/elf"
)
// Decoding the type.* symbols. This has to be in sync with // Decoding the type.* symbols. This has to be in sync with
// ../../runtime/type.go, or more specifically, with what // ../../runtime/type.go, or more specifically, with what
@ -72,14 +75,38 @@ func decodetype_ptrdata(s *LSym) int64 {
return int64(decode_inuxi(s.P[Thearch.Ptrsize:], Thearch.Ptrsize)) // 0x8 / 0x10 return int64(decode_inuxi(s.P[Thearch.Ptrsize:], Thearch.Ptrsize)) // 0x8 / 0x10
} }
// Type.commonType.gc // Find the elf.Section of a given shared library that contains a given address.
func decodetype_gcprog(s *LSym) *LSym { func findShlibSection(path string, addr uint64) *elf.Section {
if s.Type == obj.SDYNIMPORT { for _, shlib := range Ctxt.Shlibs {
// The gcprog for "type.$name" is calle "type..gcprog.$name". if shlib.Path == path {
x := "type..gcprog." + s.Name[5:] for _, sect := range shlib.File.Sections {
return Linklookup(Ctxt, x, 0) if sect.Addr <= addr && addr <= sect.Addr+sect.Size {
return sect
} }
return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) }
}
}
return nil
}
// Type.commonType.gc
func decodetype_gcprog(s *LSym) []byte {
if s.Type == obj.SDYNIMPORT {
addr := decodetype_gcprog_shlib(s)
sect := findShlibSection(s.File, addr)
if sect != nil {
// A gcprog is a 4-byte uint32 indicating length, followed by
// the actual program.
progsize := make([]byte, 4)
sect.ReadAt(progsize, int64(addr-sect.Addr))
progbytes := make([]byte, Ctxt.Arch.ByteOrder.Uint32(progsize))
sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
return append(progsize, progbytes...)
}
Exitf("cannot find gcprog for %s", s.Name)
return nil
}
return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)).P
} }
func decodetype_gcprog_shlib(s *LSym) uint64 { func decodetype_gcprog_shlib(s *LSym) uint64 {
@ -88,9 +115,16 @@ func decodetype_gcprog_shlib(s *LSym) uint64 {
func decodetype_gcmask(s *LSym) []byte { func decodetype_gcmask(s *LSym) []byte {
if s.Type == obj.SDYNIMPORT { if s.Type == obj.SDYNIMPORT {
// ldshlibsyms makes special efforts to read the value addr := decodetype_gcprog_shlib(s)
// of gcmask for types defined in that shared library. ptrdata := decodetype_ptrdata(s)
return s.gcmask sect := findShlibSection(s.File, addr)
if sect != nil {
r := make([]byte, ptrdata/int64(Thearch.Ptrsize))
sect.ReadAt(r, int64(addr-sect.Addr))
return r
}
Exitf("cannot find gcmask for %s", s.Name)
return nil
} }
mask := decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) mask := decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize))
return mask.P return mask.P

View file

@ -1178,7 +1178,7 @@ func readelfsymboldata(f *elf.File, sym *elf.Symbol) []byte {
if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE { if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE {
Diag("reading %s from non-data section", sym.Name) Diag("reading %s from non-data section", sym.Name)
} }
n, err := sect.ReadAt(data, int64(sym.Value-sect.Offset)) n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr))
if uint64(n) != sym.Size { if uint64(n) != sym.Size {
Diag("reading contents of %s: %v", sym.Name, err) Diag("reading contents of %s: %v", sym.Name, err)
} }
@ -1265,7 +1265,6 @@ func ldshlibsyms(shlib string) {
Diag("cannot open shared library: %s", libpath) Diag("cannot open shared library: %s", libpath)
return return
} }
defer f.Close()
hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG) hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG)
if err != nil { if err != nil {
@ -1280,33 +1279,15 @@ func ldshlibsyms(shlib string) {
} }
deps := strings.Split(string(depsbytes), "\n") deps := strings.Split(string(depsbytes), "\n")
syms, err := f.Symbols() syms, err := f.DynamicSymbols()
if err != nil { if err != nil {
Diag("cannot read symbols from shared library: %s", libpath) Diag("cannot read symbols from shared library: %s", libpath)
return return
} }
// If a package has a global variable of a type defined in another shared
// library, we need to know the gcmask used by the type, if any. To support
// this, we read all the runtime.gcbits.* symbols, keep a map of address to
// gcmask, and after we're read all the symbols, read the addresses of the
// gcmasks symbols out of the type data to look up the gcmask for each type.
// This depends on the fact that the runtime.gcbits.* symbols are local (so
// the address is actually present in the type data and we don't have to
// search all relocations to find the ones which correspond to gcmasks) and
// also that the shared library we are linking against has not had the symbol
// table removed.
gcmasks := make(map[uint64][]byte)
types := []*LSym{}
for _, s := range syms { for _, s := range syms {
if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION { if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION {
continue continue
} }
if strings.HasPrefix(s.Name, "runtime.gcbits.") {
gcmasks[s.Value] = readelfsymboldata(f, &s)
}
if elf.ST_BIND(s.Info) != elf.STB_GLOBAL {
continue
}
lsym := Linklookup(Ctxt, s.Name, 0) lsym := Linklookup(Ctxt, s.Name, 0)
if lsym.Type != 0 && lsym.Type != obj.SDYNIMPORT && lsym.Dupok == 0 { if lsym.Type != 0 && lsym.Type != obj.SDYNIMPORT && lsym.Dupok == 0 {
Diag( Diag(
@ -1315,28 +1296,16 @@ func ldshlibsyms(shlib string) {
} }
lsym.Type = obj.SDYNIMPORT lsym.Type = obj.SDYNIMPORT
lsym.ElfType = elf.ST_TYPE(s.Info) lsym.ElfType = elf.ST_TYPE(s.Info)
if s.Section != elf.SHN_UNDEF {
// Set .File for the library that actually defines the symbol.
lsym.File = libpath lsym.File = libpath
if strings.HasPrefix(lsym.Name, "type.") { // The decodetype_* functions in decodetype.go need access to
if f.Sections[s.Section].Type == elf.SHT_PROGBITS { // the type data.
if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") {
lsym.P = readelfsymboldata(f, &s) lsym.P = readelfsymboldata(f, &s)
} }
if !strings.HasPrefix(lsym.Name, "type..") {
types = append(types, lsym)
} }
} }
}
for _, t := range types {
if decodetype_noptr(t) != 0 || decodetype_usegcprog(t) != 0 {
continue
}
addr := decodetype_gcprog_shlib(t)
tgcmask, ok := gcmasks[addr]
if !ok {
Diag("bits not found for %s at %d", t.Name, addr)
}
t.gcmask = tgcmask
}
// We might have overwritten some functions above (this tends to happen for the // We might have overwritten some functions above (this tends to happen for the
// autogenerated type equality/hashing functions) and we don't want to generated // autogenerated type equality/hashing functions) and we don't want to generated
@ -1365,7 +1334,7 @@ func ldshlibsyms(shlib string) {
Ctxt.Etextp = last Ctxt.Etextp = last
} }
Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps}) Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
} }
func mywhatsys() { func mywhatsys() {

View file

@ -84,7 +84,6 @@ type LSym struct {
P []byte P []byte
R []Reloc R []Reloc
Local bool Local bool
gcmask []byte
} }
func (s *LSym) String() string { func (s *LSym) String() string {
@ -118,6 +117,7 @@ type Shlib struct {
Path string Path string
Hash []byte Hash []byte
Deps []string Deps []string
File *elf.File
} }
type Link struct { type Link struct {