diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 5df49b56d6..8411d2d0ac 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -20,7 +20,7 @@ const ( type Pkg struct { Name string // package name, e.g. "sys" Path string // string literal used in import statement, e.g. "runtime/internal/sys" - Pathsym *Sym + Pathsym *obj.LSym Prefix string // escaped path for use in symbol table Imported bool // export data of this package was parsed Exported bool // import line written in export data diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 2bd50b4665..70a75f9324 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -412,8 +412,6 @@ func imethods(t *Type) []*Sig { return methods } -var dimportpath_gopkg *Pkg - func dimportpath(p *Pkg) { if p.Pathsym != nil { return @@ -426,27 +424,18 @@ func dimportpath(p *Pkg) { return } - if dimportpath_gopkg == nil { - dimportpath_gopkg = mkpkg("go") - dimportpath_gopkg.Name = "go" - } - - nam := "importpath." + p.Prefix + "." - - n := Nod(ONAME, nil, nil) - n.Sym = Pkglookup(nam, dimportpath_gopkg) - - n.Class = PEXTERN - n.Xoffset = 0 - p.Pathsym = n.Sym - + var str string if p == localpkg { // Note: myimportpath != "", or else dgopkgpath won't call dimportpath. - gdatastring(n, myimportpath) + str = myimportpath } else { - gdatastring(n, p.Path) + str = p.Path } - ggloblsym(n.Sym, int32(Types[TSTRING].Width), obj.DUPOK|obj.RODATA) + + s := obj.Linklookup(Ctxt, "go.importpath."+p.Prefix+".", 0) + ot := dnameData(s, 0, str, "", nil, false) + ggloblLSym(s, int32(ot), obj.DUPOK|obj.RODATA) + p.Pathsym = s } func dgopkgpath(s *Sym, ot int, pkg *Pkg) int { @@ -469,7 +458,23 @@ func dgopkgpathLSym(s *obj.LSym, ot int, pkg *Pkg) int { } dimportpath(pkg) - return dsymptrLSym(s, ot, Linksym(pkg.Pathsym), 0) + return dsymptrLSym(s, ot, pkg.Pathsym, 0) +} + +// dgopkgpathOffLSym writes an offset relocation in s at offset ot to the pkg path symbol. +func dgopkgpathOffLSym(s *obj.LSym, ot int, pkg *Pkg) int { + if pkg == localpkg && myimportpath == "" { + // If we don't know the full import path of the package being compiled + // (i.e. -p was not passed on the compiler command line), emit a reference to + // go.importpath.""., which the linker will rewrite using the correct import path. + // Every package that imports this one directly defines the symbol. + // See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ. + ns := obj.Linklookup(Ctxt, `go.importpath."".`, 0) + return dsymptrOffLSym(s, ot, ns, 0) + } + + dimportpath(pkg) + return dsymptrOffLSym(s, ot, pkg.Pathsym, 0) } // isExportedField reports whether a struct field is exported. @@ -495,13 +500,12 @@ func dnameField(s *Sym, ot int, ft *Field) int { if ft.Note != nil { tag = *ft.Note } - return dname(s, ot, name, tag, nil, isExportedField(ft)) + nsym := dname(name, tag, nil, isExportedField(ft)) + return dsymptrLSym(Linksym(s), ot, nsym, 0) } -var dnameCount int - -// dname dumps a reflect.name for a struct field or method. -func dname(s *Sym, ot int, name, tag string, pkg *Pkg, exported bool) int { +// dnameData writes the contents of a reflect.name into s at offset ot. +func dnameData(s *obj.LSym, ot int, name, tag string, pkg *Pkg, exported bool) int { if len(name) > 1<<16-1 { Fatalf("name too long: %s", name) } @@ -534,33 +538,48 @@ func dname(s *Sym, ot int, name, tag string, pkg *Pkg, exported bool) int { copy(tb[2:], tag) } - // Very few names require a pkgPath *string (only those - // defined in a different package than their type). So if - // there is no pkgPath, we treat the name contents as string - // data that duplicates across packages. - var bsym *obj.LSym - if pkg == nil { - _, bsym = stringsym(string(b)) - } else { - // Write out data as "type.." to signal two things to the - // linker, first that when dynamically linking, the symbol - // should be moved to a relro section, and second that the - // contents should not be decoded as a type. - bsymname := fmt.Sprintf(`type..methodname."".%d`, dnameCount) - dnameCount++ - bsym = obj.Linklookup(Ctxt, bsymname, 0) - bsym.P = b - boff := len(b) - boff = int(Rnd(int64(boff), int64(Widthptr))) - boff = dgopkgpathLSym(bsym, boff, pkg) - ggloblLSym(bsym, int32(boff), obj.RODATA|obj.LOCAL) + ot = int(s.WriteBytes(Ctxt, int64(ot), b)) + + if pkg != nil { + ot = dgopkgpathOffLSym(s, ot, pkg) } - ot = dsymptrLSym(Linksym(s), ot, bsym, 0) - return ot } +var dnameCount int + +// dname creates a reflect.name for a struct field or method. +func dname(name, tag string, pkg *Pkg, exported bool) *obj.LSym { + // Write out data as "type.." to signal two things to the + // linker, first that when dynamically linking, the symbol + // should be moved to a relro section, and second that the + // contents should not be decoded as a type. + sname := "type..namedata." + if pkg == nil { + // In the common case, share data with other packages. + if name == "" { + if exported { + sname += "-noname-exported." + tag + } else { + sname += "-noname-unexported." + tag + } + } else { + sname += name + "." + tag + } + } else { + sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount) + dnameCount++ + } + s := obj.Linklookup(Ctxt, sname, 0) + if len(s.P) > 0 { + return s + } + ot := dnameData(s, 0, name, tag, pkg, exported) + ggloblLSym(s, int32(ot), obj.DUPOK|obj.RODATA) + return s +} + // dextratype dumps the fields of a runtime.uncommontype. // dataAdd is the offset in bytes after the header where the // backing array of the []method field is written (by dextratypeData). @@ -627,7 +646,8 @@ func dextratypeData(s *Sym, ot int, t *Type) int { if !exported && a.pkg != typePkg(t) { pkg = a.pkg } - ot = dname(s, ot, a.name, "", pkg, exported) + nsym := dname(a.name, "", pkg, exported) + ot = dsymptrLSym(lsym, ot, nsym, 0) ot = dmethodptrOffLSym(lsym, ot, Linksym(dtypesym(a.mtype))) ot = dmethodptrOffLSym(lsym, ot, Linksym(a.isym)) ot = dmethodptrOffLSym(lsym, ot, Linksym(a.tsym)) @@ -1213,7 +1233,8 @@ ok: if !exported && a.pkg != tpkg { pkg = a.pkg } - ot = dname(s, ot, a.name, "", pkg, exported) + nsym := dname(a.name, "", pkg, exported) + ot = dsymptrLSym(Linksym(s), ot, nsym, 0) ot = dsymptr(s, ot, dtypesym(a.type_), 0) } diff --git a/src/cmd/internal/obj/data.go b/src/cmd/internal/obj/data.go index 546ff37269..d7f0840bc1 100644 --- a/src/cmd/internal/obj/data.go +++ b/src/cmd/internal/obj/data.go @@ -75,7 +75,11 @@ func (s *LSym) prepwrite(ctxt *Link, off int64, siz int) { if s.Type == SBSS || s.Type == STLSBSS { ctxt.Diag("cannot supply data for BSS var") } - s.Grow(off + int64(siz)) + l := off + int64(siz) + s.Grow(l) + if l > s.Size { + s.Size = l + } } // WriteFloat32 writes f into s at offset off. @@ -150,6 +154,13 @@ func (s *LSym) WriteString(ctxt *Link, off int64, siz int, str string) { copy(s.P[off:off+int64(siz)], str) } +// WriteBytes writes a slice of bytes into s at offset off. +func (s *LSym) WriteBytes(ctxt *Link, off int64, b []byte) int64 { + s.prepwrite(ctxt, off, len(b)) + copy(s.P[off:], b) + return off + int64(len(b)) +} + func Addrel(s *LSym) *Reloc { s.R = append(s.R, Reloc{}) return &s.R[len(s.R)-1] diff --git a/src/reflect/type.go b/src/reflect/type.go index c7ed402be2..3c7affcd7f 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -299,9 +299,9 @@ type method struct { // Using a pointer to this struct reduces the overall size required // to describe an unnamed type with no methods. type uncommonType struct { - pkgPath *string // import path; nil for built-in types like int, string - mcount uint16 // number of methods - moff uint16 // offset from this uncommontype to [mcount]method + pkgPath name // import path; empty for built-in types like int, string + mcount uint16 // number of methods + moff uint16 // offset from this uncommontype to [mcount]method } // ChanDir represents a channel type's direction. @@ -354,7 +354,7 @@ type imethod struct { // interfaceType represents an interface type. type interfaceType struct { rtype `reflect:"interface"` - pkgPath *string // import path + pkgPath name // import path methods []imethod // sorted by hash } @@ -396,7 +396,7 @@ type structField struct { // structType represents a struct type. type structType struct { rtype `reflect:"struct"` - pkgPath *string + pkgPath name fields []structField // sorted by offset } @@ -406,7 +406,7 @@ type structType struct { // // 1<<0 the name is exported // 1<<1 tag data follows the name -// 1<<2 pkgPath *string follow the name and tag +// 1<<2 pkgPath nameOff follows the name and tag // // The next two bytes are the data length: // @@ -417,10 +417,9 @@ type structType struct { // If tag data follows then bytes 3+l and 3+l+1 are the tag length, // with the data following. // -// If the import path follows, then ptrSize bytes at the end of -// the data form a *string. The pointer is aligned to its width. -// The import path is only set for concrete methods that are defined -// in a different package than their type. +// If the import path follows, then 4 bytes at the end of +// the data form a nameOff. The import path is only set for concrete +// methods that are defined in a different package than their type. type name struct { bytes *byte } @@ -446,6 +445,9 @@ func (n *name) tagLen() int { } func (n *name) name() (s string) { + if n.bytes == nil { + return "" + } nl := n.nameLen() if nl == 0 { return "" @@ -468,16 +470,18 @@ func (n *name) tag() (s string) { return s } -func (n *name) pkgPath() *string { - if *n.data(0)&(1<<2) == 0 { - return nil +func (n *name) pkgPath() string { + if n.bytes == nil || *n.data(0)&(1<<2) == 0 { + return "" } off := 3 + n.nameLen() if tl := n.tagLen(); tl > 0 { off += 2 + tl } - off = int(round(uintptr(off), ptrSize)) - return *(**string)(unsafe.Pointer(n.data(off))) + var nameOff int32 + copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:]) + pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n), nameOff))} + return pkgPathName.name() } // round n up to a multiple of a. a must be a power of 2. @@ -595,10 +599,10 @@ func (t *uncommonType) methods() []method { } func (t *uncommonType) PkgPath() string { - if t == nil || t.pkgPath == nil { + if t == nil { return "" } - return *t.pkgPath + return t.pkgPath.name() } // resolveTypeOff resolves an *rtype offset from a base type. @@ -752,11 +756,10 @@ func (t *rtype) Method(i int) (m Method) { m.Name = p.name.name() fl := flag(Func) if !p.name.isExported() { - pkgPath := p.name.pkgPath() - if pkgPath == nil { - pkgPath = ut.pkgPath + m.PkgPath = p.name.pkgPath() + if m.PkgPath == "" { + m.PkgPath = ut.pkgPath.name() } - m.PkgPath = *pkgPath fl |= flagStickyRO } if p.mtyp != 0 { @@ -1004,11 +1007,10 @@ func (t *interfaceType) Method(i int) (m Method) { p := &t.methods[i] m.Name = p.name.name() if !p.name.isExported() { - pkgPath := p.name.pkgPath() - if pkgPath == nil { - pkgPath = t.pkgPath + m.PkgPath = p.name.pkgPath() + if m.PkgPath == "" { + m.PkgPath = t.pkgPath.name() } - m.PkgPath = *pkgPath } m.Type = toType(p.typ) m.Index = i @@ -1146,9 +1148,9 @@ func (t *structType) Field(i int) (f StructField) { f.Name = t.Name() f.Anonymous = true } - if t.pkgPath != nil && !p.name.isExported() { + if !p.name.isExported() { // Fields never have an import path in their name. - f.PkgPath = *t.pkgPath + f.PkgPath = t.pkgPath.name() } if tag := p.name.tag(); tag != "" { f.Tag = StructTag(tag) @@ -2325,7 +2327,7 @@ func StructOf(fields []StructField) Type { case Interface: ift := (*interfaceType)(unsafe.Pointer(ft)) for im, m := range ift.methods { - if m.name.pkgPath() != nil { + if m.name.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } @@ -2384,7 +2386,7 @@ func StructOf(fields []StructField) Type { ptr := (*ptrType)(unsafe.Pointer(ft)) if unt := ptr.uncommon(); unt != nil { for _, m := range unt.methods() { - if m.name.pkgPath() != nil { + if m.name.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } @@ -2398,7 +2400,7 @@ func StructOf(fields []StructField) Type { } if unt := ptr.elem.uncommon(); unt != nil { for _, m := range unt.methods() { - if m.name.pkgPath() != nil { + if m.name.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } @@ -2413,7 +2415,7 @@ func StructOf(fields []StructField) Type { default: if unt := ft.uncommon(); unt != nil { for _, m := range unt.methods() { - if m.name.pkgPath() != nil { + if m.name.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index 2410b1954a..adfd660847 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -183,10 +183,11 @@ func dumptype(t *_type) { dumpint(tagType) dumpint(uint64(uintptr(unsafe.Pointer(t)))) dumpint(uint64(t.size)) - if x := t.uncommon(); x == nil || x.pkgpath == nil { + if x := t.uncommon(); x == nil || x.pkgpath.name() == "" { dumpstr(t._string) } else { - pkgpath := stringStructOf(x.pkgpath) + pkgpathstr := x.pkgpath.name() + pkgpath := stringStructOf(&pkgpathstr) namestr := t.name() name := stringStructOf(&namestr) dumpint(uint64(uintptr(pkgpath.len) + 1 + uintptr(name.len))) diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 700bdc2f48..84f0ee8f0c 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -101,15 +101,15 @@ func additab(m *itab, locked, canfail bool) { iname := i.name.name() itype := i._type ipkg := i.name.pkgPath() - if ipkg == nil { - ipkg = inter.pkgpath + if ipkg == "" { + ipkg = inter.pkgpath.name() } for ; j < nt; j++ { t := &xmhdr[j] if typ.typeOff(t.mtyp) == itype && t.name.name() == iname { pkgPath := t.name.pkgPath() - if pkgPath == nil { - pkgPath = x.pkgpath + if pkgPath == "" { + pkgPath = x.pkgpath.name() } if t.name.isExported() || pkgPath == ipkg { if m != nil { diff --git a/src/runtime/type.go b/src/runtime/type.go index 86131d3ff3..711753bab5 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -6,10 +6,7 @@ package runtime -import ( - "runtime/internal/sys" - "unsafe" -) +import "unsafe" // tflag is documented in ../reflect/type.go. type tflag uint8 @@ -151,6 +148,33 @@ var reflectOffs struct { minv map[unsafe.Pointer]int32 } +func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name { + if off == 0 { + return name{} + } + base := uintptr(ptrInModule) + var md *moduledata + for next := &firstmoduledata; next != nil; next = next.next { + if base >= next.types && base < next.etypes { + md = next + break + } + } + if md == nil { + println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:") + for next := &firstmoduledata; next != nil; next = next.next { + println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) + } + throw("runtime: name offset base pointer out of range") + } + res := md.types + uintptr(off) + if res > md.etypes { + println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes)) + throw("runtime: name offset out of range") + } + return name{(*byte)(unsafe.Pointer(res))} +} + func (t *_type) typeOff(off typeOff) *_type { if off == 0 { return nil @@ -240,6 +264,7 @@ func (t *functype) dotdotdot() bool { return t.outCount&(1<<15) != 0 } +type nameOff int32 type typeOff int32 type textOff int32 @@ -251,7 +276,7 @@ type method struct { } type uncommontype struct { - pkgpath *string + pkgpath name mcount uint16 // number of methods moff uint16 // offset from this uncommontype to [mcount]method } @@ -263,7 +288,7 @@ type imethod struct { type interfacetype struct { typ _type - pkgpath *string + pkgpath name mhdr []imethod } @@ -319,7 +344,7 @@ type structfield struct { type structtype struct { typ _type - pkgPath *string + pkgPath name fields []structfield } @@ -350,6 +375,9 @@ func (n *name) tagLen() int { } func (n *name) name() (s string) { + if n.bytes == nil { + return "" + } nl := n.nameLen() if nl == 0 { return "" @@ -372,16 +400,18 @@ func (n *name) tag() (s string) { return s } -func (n *name) pkgPath() *string { - if *n.data(0)&(1<<2) == 0 { - return nil +func (n *name) pkgPath() string { + if n.bytes == nil || *n.data(0)&(1<<2) == 0 { + return "" } off := 3 + n.nameLen() if tl := n.tagLen(); tl > 0 { off += 2 + tl } - off = int(round(uintptr(off), sys.PtrSize)) - return *(**string)(unsafe.Pointer(n.data(off))) + var nameOff nameOff + copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:]) + pkgPathName := resolveNameOff(unsafe.Pointer(n.bytes), nameOff) + return pkgPathName.name() } // typelinksinit scans the types from extra modules and builds the @@ -466,7 +496,7 @@ func typesEqual(t, v *_type) bool { if ut == nil || uv == nil { return false } - if !pkgPathEqual(ut.pkgpath, uv.pkgpath) { + if ut.pkgpath.name() != uv.pkgpath.name() { return false } } @@ -506,7 +536,7 @@ func typesEqual(t, v *_type) bool { case kindInterface: it := (*interfacetype)(unsafe.Pointer(t)) iv := (*interfacetype)(unsafe.Pointer(v)) - if !pkgPathEqual(it.pkgpath, iv.pkgpath) { + if it.pkgpath.name() != iv.pkgpath.name() { return false } if len(it.mhdr) != len(iv.mhdr) { @@ -518,7 +548,7 @@ func typesEqual(t, v *_type) bool { if tm.name.name() != vm.name.name() { return false } - if !pkgPathEqual(tm.name.pkgPath(), vm.name.pkgPath()) { + if tm.name.pkgPath() != vm.name.pkgPath() { return false } if !typesEqual(tm._type, vm._type) { @@ -550,7 +580,7 @@ func typesEqual(t, v *_type) bool { if tf.name.name() != vf.name.name() { return false } - if !pkgPathEqual(tf.name.pkgPath(), vf.name.pkgPath()) { + if tf.name.pkgPath() != vf.name.pkgPath() { return false } if !typesEqual(tf.typ, vf.typ) { @@ -570,13 +600,3 @@ func typesEqual(t, v *_type) bool { return false } } - -func pkgPathEqual(p, q *string) bool { - if p == q { - return true - } - if p == nil || q == nil { - return false - } - return *p == *q -}