From: Michael Hudson-Doyle Date: Wed, 1 Apr 2015 03:20:44 +0000 (+1300) Subject: cmd/internal/gc, cmd/internal/ld: fixes for global vars of types from other modules X-Git-Tag: go1.5beta1~956 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=7820d270498d6d4d960f7e34a0a80cafe2d05117;p=gostls13.git cmd/internal/gc, cmd/internal/ld: fixes for global vars of types from other modules To make the gcprog for global data containing variables of types defined in other shared libraries, we need to know a lot about those types. So read the value of any symbol with a name starting with "type.". If a type uses a mask, the name of the symbol defining the mask unfortunately cannot be predicted from the type name so I have to keep track of the addresses of every such symbol and associate them with the type symbols after the fact. I'm not very happy about this change, but something like this is needed and this is as pleasant as I know how to make it. Change-Id: I408d831b08b3b31e0610688c41367b23998e975c Reviewed-on: https://go-review.googlesource.com/8334 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor --- diff --git a/src/cmd/internal/ld/decodesym.go b/src/cmd/internal/ld/decodesym.go index 4bad549fd4..19d2502c7a 100644 --- a/src/cmd/internal/ld/decodesym.go +++ b/src/cmd/internal/ld/decodesym.go @@ -67,10 +67,20 @@ func decodetype_size(s *LSym) int64 { // Type.commonType.gc func decodetype_gcprog(s *LSym) *LSym { + if s.Type == obj.SDYNIMPORT { + // The gcprog for "type.$name" is calle "type..gcprog.$name". + x := "type..gcprog." + s.Name[5:] + return Linklookup(Ctxt, x, 0) + } return decode_reloc_sym(s, 1*int32(Thearch.Ptrsize)+8+2*int32(Thearch.Ptrsize)) } func decodetype_gcmask(s *LSym) []byte { + if s.Type == obj.SDYNIMPORT { + // ldshlibsyms makes special efforts to read the value + // of gcmask for types defined in that shared library. + return s.gcmask + } mask := decode_reloc_sym(s, 1*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) return mask.P } diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go index 3d466e5e18..3f7b04fbbb 100644 --- a/src/cmd/internal/ld/lib.go +++ b/src/cmd/internal/ld/lib.go @@ -1136,11 +1136,23 @@ func ldshlibsyms(shlib string) { return } defer f.Close() - syms, err := f.DynamicSymbols() + syms, err := f.Symbols() if err != nil { Diag("cannot read symbols from shared library: %s", libpath) 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 { if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION { continue @@ -1151,6 +1163,20 @@ func ldshlibsyms(shlib string) { if strings.HasPrefix(s.Name, "_") { continue } + if strings.HasPrefix(s.Name, "runtime.gcbits.0x") { + data := make([]byte, s.Size) + sect := f.Sections[s.Section] + if sect.Type == elf.SHT_PROGBITS { + n, err := sect.ReadAt(data, int64(s.Value-sect.Offset)) + if uint64(n) != s.Size { + Diag("Error reading contents of %s: %v", s.Name, err) + } + } + gcmasks[s.Value] = data + } + if elf.ST_BIND(s.Info) != elf.STB_GLOBAL { + continue + } lsym := Linklookup(Ctxt, s.Name, 0) if lsym.Type != 0 && lsym.Dupok == 0 { Diag( @@ -1159,6 +1185,35 @@ func ldshlibsyms(shlib string) { } lsym.Type = obj.SDYNIMPORT lsym.File = libpath + if strings.HasPrefix(lsym.Name, "type.") { + data := make([]byte, s.Size) + sect := f.Sections[s.Section] + if sect.Type == elf.SHT_PROGBITS { + n, err := sect.ReadAt(data, int64(s.Value-sect.Offset)) + if uint64(n) != s.Size { + Diag("Error reading contents of %s: %v", s.Name, err) + } + lsym.P = data + } + if !strings.HasPrefix(lsym.Name, "type..") { + types = append(types, lsym) + } + } + } + + for _, t := range types { + if decodetype_noptr(t) != 0 || decodetype_usegcprog(t) != 0 { + continue + } + // The expression on the next line is a copy of the expression from + // decodetype_gcmask in decodesym.go, which in turn depends on details of + // how the type data is laid out, as seen in gc/reflect.go:dcommontype. + addr := decode_inuxi(t.P[1*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize):], Thearch.Ptrsize) + 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 diff --git a/src/cmd/internal/ld/link.go b/src/cmd/internal/ld/link.go index fd480733f7..a92ab59499 100644 --- a/src/cmd/internal/ld/link.go +++ b/src/cmd/internal/ld/link.go @@ -75,6 +75,7 @@ type LSym struct { P []byte R []Reloc Local bool + gcmask []byte } type Reloc struct { diff --git a/src/cmd/internal/ld/objfile.go b/src/cmd/internal/ld/objfile.go index c424cdca8c..1e45d72fd8 100644 --- a/src/cmd/internal/ld/objfile.go +++ b/src/cmd/internal/ld/objfile.go @@ -340,6 +340,8 @@ func rdsym(ctxt *Link, f *Biobuf, pkg string) *LSym { s.Reachable = false } } - + if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.0x") { + s.Local = true + } return s }