AddBytes(s Sym, b []byte)
AddAddress(s Sym, t interface{}, ofs int64)
AddSectionOffset(s Sym, size int, t interface{}, ofs int64)
+ AddDWARFSectionOffset(s Sym, size int, t interface{}, ofs int64)
CurrentOffset(s Sym) int64
RecordDclReference(from Sym, to Sym, dclIdx int, inlIndex int)
RecordChildDieOffsets(s Sym, vars []*Var, offsets []int32)
// Attribute for DW_TAG_member of a struct type.
// Nonzero value indicates the struct field is an embedded field.
DW_AT_go_embedded_field = 0x2903
+ DW_AT_go_runtime_type = 0x2904
DW_AT_internal_location = 253 // params and locals; not emitted
)
{DW_AT_encoding, DW_FORM_data1},
{DW_AT_byte_size, DW_FORM_data1},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
},
},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_byte_size, DW_FORM_udata},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
},
},
{DW_AT_name, DW_FORM_string},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
{DW_AT_go_elem, DW_FORM_ref_addr},
},
},
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_byte_size, DW_FORM_udata},
- // {DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
},
},
{DW_AT_name, DW_FORM_string},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
},
},
{DW_AT_name, DW_FORM_string},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
{DW_AT_go_key, DW_FORM_ref_addr},
{DW_AT_go_elem, DW_FORM_ref_addr},
},
{DW_AT_name, DW_FORM_string},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
},
},
{DW_AT_name, DW_FORM_string},
{DW_AT_byte_size, DW_FORM_udata},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
{DW_AT_go_elem, DW_FORM_ref_addr},
},
},
{DW_AT_name, DW_FORM_string},
{DW_AT_byte_size, DW_FORM_udata},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
},
},
{DW_AT_name, DW_FORM_string},
{DW_AT_byte_size, DW_FORM_udata},
{DW_AT_go_kind, DW_FORM_data1},
+ {DW_AT_go_runtime_type, DW_FORM_addr},
},
},
func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, data interface{}) error {
switch form {
case DW_FORM_addr: // address
+ // Allow nil addresses for DW_AT_go_runtime_type.
+ if data == nil && value == 0 {
+ ctxt.AddInt(s, ctxt.PtrSize(), 0)
+ break
+ }
+ if cls == DW_CLS_GO_TYPEREF {
+ ctxt.AddSectionOffset(s, ctxt.PtrSize(), data, value)
+ break
+ }
ctxt.AddAddress(s, data, value)
case DW_FORM_block1: // block
case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr
if cls == DW_CLS_PTR { // DW_AT_stmt_list and DW_AT_ranges
- ctxt.AddSectionOffset(s, 4, data, value)
+ ctxt.AddDWARFSectionOffset(s, 4, data, value)
break
}
ctxt.AddInt(s, 4, value)
if data == nil {
return fmt.Errorf("dwarf: null reference in %d", abbrev)
}
- ctxt.AddSectionOffset(s, 4, data, value)
+ ctxt.AddDWARFSectionOffset(s, 4, data, value)
case DW_FORM_ref1, // reference within the compilation unit
DW_FORM_ref2, // reference
DW_CLS_REFERENCE
DW_CLS_ADDRLOC
DW_CLS_STRING
+
+ // Go-specific internal hackery.
+ DW_CLS_GO_TYPEREF
)
// Table 20
}
}
func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+ panic("should be used only in the linker")
+}
+func (c dwCtxt) AddDWARFSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
ls := s.(*LSym)
rsym := t.(*LSym)
ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
case objabi.R_ADDROFF:
// The method offset tables using this relocation expect the offset to be relative
// to the start of the first text section, even if there are multiple.
-
if r.Sym.Sect.Name == ".text" {
o = Symaddr(r.Sym) - int64(Segtext.Sections[0].Vaddr) + r.Add
} else {
if false {
nam := "<nil>"
+ var addr int64
if r.Sym != nil {
nam = r.Sym.Name
+ addr = Symaddr(r.Sym)
+ }
+ xnam := "<nil>"
+ if r.Xsym != nil {
+ xnam = r.Xsym.Name
}
- fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x [type %d (%s)/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, Symaddr(r.Sym), r.Add, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Variant, o)
+ fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x (xsym: %s +%#x) [type %d (%s)/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, addr, r.Add, xnam, r.Xadd, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Variant, o)
}
switch siz {
default:
ls.AddAddrPlus4(t.(*sym.Symbol), 0)
}
r := &ls.R[len(ls.R)-1]
- r.Type = objabi.R_DWARFSECREF
+ r.Type = objabi.R_ADDROFF
r.Add = ofs
}
+func (c dwctxt) AddDWARFSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+ c.AddSectionOffset(s, size, t, ofs)
+ ls := s.(*sym.Symbol)
+ ls.R[len(ls.R)-1].Type = objabi.R_DWARFSECREF
+}
+
func (c dwctxt) Logf(format string, args ...interface{}) {
c.linkctxt.Logf(format, args...)
}
}
newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0)
+ if gotype.Attr.Reachable() {
+ newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype)
+ }
if _, ok := prototypedies[gotype.Name]; ok {
prototypedies[gotype.Name] = die
// Find or construct *T given T.
func defptrto(ctxt *Link, dwtype *sym.Symbol) *sym.Symbol {
ptrname := "*" + nameFromDIESym(dwtype)
- die := find(ctxt, ptrname)
- if die == nil {
- pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
- newrefattr(pdie, dwarf.DW_AT_type, dwtype)
- return dtolsym(pdie.Sym)
+ if die := find(ctxt, ptrname); die != nil {
+ return die
}
- return die
+ pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
+ newrefattr(pdie, dwarf.DW_AT_type, dwtype)
+
+ // The DWARF info synthesizes pointer types that don't exist at the
+ // language level, like *hash<...> and *bucket<...>, and the data
+ // pointers of slices. Link to the ones we can find.
+ gotype := ctxt.Syms.ROLookup("type."+ptrname, 0)
+ if gotype != nil && gotype.Attr.Reachable() {
+ newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype)
+ }
+ return dtolsym(pdie.Sym)
}
// Copies src's children into dst. Copies attributes by value.
newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(ctxt.Arch.PtrSize), 0)
newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0)
+ newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, lookupOrDiag(ctxt, "type.uintptr"))
// Prototypes needed for type synthesis.
prototypedies = map[string]*dwarf.DWDie{
"path/filepath"
"reflect"
"runtime"
+ "strconv"
"testing"
)
const (
NoOpt = "-gcflags=-l -N"
- Opt = ""
OptInl4 = "-gcflags=all=-l=4"
OptInl4DwLoc = "-gcflags=all=-l=4 -dwarflocationlists"
)
-func TestRuntimeTypeDIEs(t *testing.T) {
+func TestRuntimeTypesPresent(t *testing.T) {
testenv.MustHaveGoBuild(t)
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; no DWARF symbol table in executables")
}
- dir, err := ioutil.TempDir("", "TestRuntimeTypeDIEs")
+ dir, err := ioutil.TempDir("", "TestRuntimeTypesPresent")
if err != nil {
t.Fatalf("could not create directory: %v", err)
}
return
}
-func gobuild(t *testing.T, dir string, testfile string, gcflags string) *objfilepkg.File {
+type builtFile struct {
+ *objfilepkg.File
+ path string
+}
+
+func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile {
src := filepath.Join(dir, "test.go")
- dst := filepath.Join(dir, "out")
+ dst := filepath.Join(dir, "out.exe")
if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil {
t.Fatal(err)
if err != nil {
t.Fatal(err)
}
- return f
+ return &builtFile{f, dst}
}
func TestEmbeddedStructMarker(t *testing.T) {
abstractOriginSanity(t, OptInl4DwLoc)
}
+
+func TestRuntimeTypeAttr(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ // Explicitly test external linking, for dsymutil compatility on Darwin.
+ for _, flags := range []string{"-ldflags=linkmode=internal", "-ldflags=-linkmode=external"} {
+ t.Run("flags="+flags, func(t *testing.T) {
+ testRuntimeTypeAttr(t, flags)
+ })
+ }
+}
+
+func testRuntimeTypeAttr(t *testing.T, flags string) {
+ const prog = `
+package main
+
+import "unsafe"
+
+type X struct{ _ int }
+
+func main() {
+ var x interface{} = &X{}
+ p := *(*uintptr)(unsafe.Pointer(&x))
+ print(p)
+}
+`
+ dir, err := ioutil.TempDir("", "TestRuntimeType")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ f := gobuild(t, dir, prog, flags)
+ out, err := exec.Command(f.path).CombinedOutput()
+ if err != nil {
+ t.Fatalf("could not run test program: %v", err)
+ }
+ addr, err := strconv.ParseUint(string(out), 10, 64)
+ if err != nil {
+ t.Fatalf("could not parse type address from program output %q: %v", out, err)
+ }
+
+ symbols, err := f.Symbols()
+ if err != nil {
+ t.Fatalf("error reading symbols: %v", err)
+ }
+ var typeStar *objfilepkg.Sym
+ for _, sym := range symbols {
+ if sym.Name == "type.*" {
+ typeStar = &sym
+ break
+ }
+ }
+ if typeStar == nil {
+ t.Fatal("couldn't find types.* in symbols")
+ }
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := examiner{}
+ if err := ex.populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ dies := ex.Named("*main.X")
+ if len(dies) != 1 {
+ t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies))
+ }
+ rtAttr := dies[0].Val(0x2904)
+ if rtAttr == nil {
+ t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
+ }
+
+ if rtAttr.(uint64)+typeStar.Addr != addr {
+ t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), typeStar.Addr, addr)
+ }
+}