Commit 393f84a1 authored by Heschi Kreinick's avatar Heschi Kreinick

cmd/ld: link to runtime types from DWARF

Add a new DWARF attribute, DW_AT_go_runtime_type, that gives the offset
of the runtime type structure, if any, for a DWARF type. This should
allow debuggers to decode interface content without having to do awkward
name matching.

Fixes #24814

Change-Id: Ic7a66524d2be484154c584afa9697111618efea4
Reviewed-on: https://go-review.googlesource.com/106775Reviewed-by: default avatarAlessandro Arzilli <alessandro.arzilli@gmail.com>
Reviewed-by: default avatarDavid Chase <drchase@google.com>
parent 568d6f98
...@@ -178,6 +178,7 @@ type Context interface { ...@@ -178,6 +178,7 @@ type Context interface {
AddBytes(s Sym, b []byte) AddBytes(s Sym, b []byte)
AddAddress(s Sym, t interface{}, ofs int64) AddAddress(s Sym, t interface{}, ofs int64)
AddSectionOffset(s Sym, size int, 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 CurrentOffset(s Sym) int64
RecordDclReference(from Sym, to Sym, dclIdx int, inlIndex int) RecordDclReference(from Sym, to Sym, dclIdx int, inlIndex int)
RecordChildDieOffsets(s Sym, vars []*Var, offsets []int32) RecordChildDieOffsets(s Sym, vars []*Var, offsets []int32)
...@@ -291,6 +292,7 @@ const ( ...@@ -291,6 +292,7 @@ const (
// Attribute for DW_TAG_member of a struct type. // Attribute for DW_TAG_member of a struct type.
// Nonzero value indicates the struct field is an embedded field. // Nonzero value indicates the struct field is an embedded field.
DW_AT_go_embedded_field = 0x2903 DW_AT_go_embedded_field = 0x2903
DW_AT_go_runtime_type = 0x2904
DW_AT_internal_location = 253 // params and locals; not emitted DW_AT_internal_location = 253 // params and locals; not emitted
) )
...@@ -642,6 +644,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -642,6 +644,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_encoding, DW_FORM_data1}, {DW_AT_encoding, DW_FORM_data1},
{DW_AT_byte_size, DW_FORM_data1}, {DW_AT_byte_size, DW_FORM_data1},
{DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_kind, DW_FORM_data1},
{DW_AT_go_runtime_type, DW_FORM_addr},
}, },
}, },
...@@ -655,6 +658,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -655,6 +658,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_type, DW_FORM_ref_addr}, {DW_AT_type, DW_FORM_ref_addr},
{DW_AT_byte_size, DW_FORM_udata}, {DW_AT_byte_size, DW_FORM_udata},
{DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_kind, DW_FORM_data1},
{DW_AT_go_runtime_type, DW_FORM_addr},
}, },
}, },
...@@ -666,6 +670,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -666,6 +670,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_name, DW_FORM_string}, {DW_AT_name, DW_FORM_string},
{DW_AT_type, DW_FORM_ref_addr}, {DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1}, {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_go_elem, DW_FORM_ref_addr},
}, },
}, },
...@@ -677,8 +682,8 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -677,8 +682,8 @@ var abbrevs = [DW_NABRV]dwAbbrev{
[]dwAttrForm{ []dwAttrForm{
{DW_AT_name, DW_FORM_string}, {DW_AT_name, DW_FORM_string},
{DW_AT_byte_size, DW_FORM_udata}, {DW_AT_byte_size, DW_FORM_udata},
// {DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_kind, DW_FORM_data1},
{DW_AT_go_runtime_type, DW_FORM_addr},
}, },
}, },
...@@ -690,6 +695,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -690,6 +695,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_name, DW_FORM_string}, {DW_AT_name, DW_FORM_string},
{DW_AT_type, DW_FORM_ref_addr}, {DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_kind, DW_FORM_data1},
{DW_AT_go_runtime_type, DW_FORM_addr},
}, },
}, },
...@@ -701,6 +707,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -701,6 +707,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_name, DW_FORM_string}, {DW_AT_name, DW_FORM_string},
{DW_AT_type, DW_FORM_ref_addr}, {DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1}, {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_key, DW_FORM_ref_addr},
{DW_AT_go_elem, DW_FORM_ref_addr}, {DW_AT_go_elem, DW_FORM_ref_addr},
}, },
...@@ -714,6 +721,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -714,6 +721,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_name, DW_FORM_string}, {DW_AT_name, DW_FORM_string},
{DW_AT_type, DW_FORM_ref_addr}, {DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_kind, DW_FORM_data1},
{DW_AT_go_runtime_type, DW_FORM_addr},
}, },
}, },
...@@ -734,6 +742,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -734,6 +742,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_name, DW_FORM_string}, {DW_AT_name, DW_FORM_string},
{DW_AT_byte_size, DW_FORM_udata}, {DW_AT_byte_size, DW_FORM_udata},
{DW_AT_go_kind, DW_FORM_data1}, {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_go_elem, DW_FORM_ref_addr},
}, },
}, },
...@@ -746,6 +755,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -746,6 +755,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_name, DW_FORM_string}, {DW_AT_name, DW_FORM_string},
{DW_AT_byte_size, DW_FORM_udata}, {DW_AT_byte_size, DW_FORM_udata},
{DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_kind, DW_FORM_data1},
{DW_AT_go_runtime_type, DW_FORM_addr},
}, },
}, },
...@@ -757,6 +767,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -757,6 +767,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_name, DW_FORM_string}, {DW_AT_name, DW_FORM_string},
{DW_AT_byte_size, DW_FORM_udata}, {DW_AT_byte_size, DW_FORM_udata},
{DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_kind, DW_FORM_data1},
{DW_AT_go_runtime_type, DW_FORM_addr},
}, },
}, },
...@@ -818,6 +829,15 @@ type DWDie struct { ...@@ -818,6 +829,15 @@ type DWDie struct {
func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, data interface{}) error { func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, data interface{}) error {
switch form { switch form {
case DW_FORM_addr: // address 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) ctxt.AddAddress(s, data, value)
case DW_FORM_block1: // block case DW_FORM_block1: // block
...@@ -861,7 +881,7 @@ func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, da ...@@ -861,7 +881,7 @@ func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, da
case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr
if cls == DW_CLS_PTR { // DW_AT_stmt_list and DW_AT_ranges 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 break
} }
ctxt.AddInt(s, 4, value) ctxt.AddInt(s, 4, value)
...@@ -898,7 +918,7 @@ func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, da ...@@ -898,7 +918,7 @@ func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, da
if data == nil { if data == nil {
return fmt.Errorf("dwarf: null reference in %d", abbrev) 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 case DW_FORM_ref1, // reference within the compilation unit
DW_FORM_ref2, // reference DW_FORM_ref2, // reference
......
...@@ -93,6 +93,9 @@ const ( ...@@ -93,6 +93,9 @@ const (
DW_CLS_REFERENCE DW_CLS_REFERENCE
DW_CLS_ADDRLOC DW_CLS_ADDRLOC
DW_CLS_STRING DW_CLS_STRING
// Go-specific internal hackery.
DW_CLS_GO_TYPEREF
) )
// Table 20 // Table 20
......
...@@ -457,6 +457,9 @@ func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { ...@@ -457,6 +457,9 @@ func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
} }
} }
func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { 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) ls := s.(*LSym)
rsym := t.(*LSym) rsym := t.(*LSym)
ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs) ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
......
...@@ -362,7 +362,6 @@ func relocsym(ctxt *Link, s *sym.Symbol) { ...@@ -362,7 +362,6 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
case objabi.R_ADDROFF: case objabi.R_ADDROFF:
// The method offset tables using this relocation expect the offset to be relative // 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. // to the start of the first text section, even if there are multiple.
if r.Sym.Sect.Name == ".text" { if r.Sym.Sect.Name == ".text" {
o = Symaddr(r.Sym) - int64(Segtext.Sections[0].Vaddr) + r.Add o = Symaddr(r.Sym) - int64(Segtext.Sections[0].Vaddr) + r.Add
} else { } else {
...@@ -450,10 +449,16 @@ func relocsym(ctxt *Link, s *sym.Symbol) { ...@@ -450,10 +449,16 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
if false { if false {
nam := "<nil>" nam := "<nil>"
var addr int64
if r.Sym != nil { if r.Sym != nil {
nam = r.Sym.Name 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 { switch siz {
default: default:
......
...@@ -61,10 +61,16 @@ func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64 ...@@ -61,10 +61,16 @@ func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
ls.AddAddrPlus4(t.(*sym.Symbol), 0) ls.AddAddrPlus4(t.(*sym.Symbol), 0)
} }
r := &ls.R[len(ls.R)-1] r := &ls.R[len(ls.R)-1]
r.Type = objabi.R_DWARFSECREF r.Type = objabi.R_ADDROFF
r.Add = ofs 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{}) { func (c dwctxt) Logf(format string, args ...interface{}) {
c.linkctxt.Logf(format, args...) c.linkctxt.Logf(format, args...)
} }
...@@ -546,6 +552,9 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { ...@@ -546,6 +552,9 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
} }
newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0) 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 { if _, ok := prototypedies[gotype.Name]; ok {
prototypedies[gotype.Name] = die prototypedies[gotype.Name] = die
...@@ -561,14 +570,21 @@ func nameFromDIESym(dwtype *sym.Symbol) string { ...@@ -561,14 +570,21 @@ func nameFromDIESym(dwtype *sym.Symbol) string {
// Find or construct *T given T. // Find or construct *T given T.
func defptrto(ctxt *Link, dwtype *sym.Symbol) *sym.Symbol { func defptrto(ctxt *Link, dwtype *sym.Symbol) *sym.Symbol {
ptrname := "*" + nameFromDIESym(dwtype) ptrname := "*" + nameFromDIESym(dwtype)
die := find(ctxt, ptrname) if die := find(ctxt, ptrname); die != nil {
if die == nil { return die
pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
newrefattr(pdie, dwarf.DW_AT_type, dwtype)
return dtolsym(pdie.Sym)
} }
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. // Copies src's children into dst. Copies attributes by value.
...@@ -1692,6 +1708,7 @@ func dwarfgeneratedebugsyms(ctxt *Link) { ...@@ -1692,6 +1708,7 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) 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_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_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. // Prototypes needed for type synthesis.
prototypedies = map[string]*dwarf.DWDie{ prototypedies = map[string]*dwarf.DWDie{
......
...@@ -16,24 +16,24 @@ import ( ...@@ -16,24 +16,24 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"strconv"
"testing" "testing"
) )
const ( const (
NoOpt = "-gcflags=-l -N" NoOpt = "-gcflags=-l -N"
Opt = ""
OptInl4 = "-gcflags=all=-l=4" OptInl4 = "-gcflags=all=-l=4"
OptInl4DwLoc = "-gcflags=all=-l=4 -dwarflocationlists" OptInl4DwLoc = "-gcflags=all=-l=4 -dwarflocationlists"
) )
func TestRuntimeTypeDIEs(t *testing.T) { func TestRuntimeTypesPresent(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
if runtime.GOOS == "plan9" { if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; no DWARF symbol table in executables") t.Skip("skipping on plan9; no DWARF symbol table in executables")
} }
dir, err := ioutil.TempDir("", "TestRuntimeTypeDIEs") dir, err := ioutil.TempDir("", "TestRuntimeTypesPresent")
if err != nil { if err != nil {
t.Fatalf("could not create directory: %v", err) t.Fatalf("could not create directory: %v", err)
} }
...@@ -84,9 +84,14 @@ func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[st ...@@ -84,9 +84,14 @@ func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[st
return 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") 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 { if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil {
t.Fatal(err) t.Fatal(err)
...@@ -102,7 +107,7 @@ func gobuild(t *testing.T, dir string, testfile string, gcflags string) *objfile ...@@ -102,7 +107,7 @@ func gobuild(t *testing.T, dir string, testfile string, gcflags string) *objfile
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return f return &builtFile{f, dst}
} }
func TestEmbeddedStructMarker(t *testing.T) { func TestEmbeddedStructMarker(t *testing.T) {
...@@ -804,3 +809,87 @@ func TestAbstractOriginSanityWithLocationLists(t *testing.T) { ...@@ -804,3 +809,87 @@ func TestAbstractOriginSanityWithLocationLists(t *testing.T) {
abstractOriginSanity(t, OptInl4DwLoc) 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)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment