internal/gocore: update for Go 1.18 All of the changes required are related to josharian@'s series removing relocations from FUNCDATA, etc (CL 352110, CL 353138, CL 352191). I am least confident about the changes in Process.readFrame. Some functions are missing the LocalsPointerMap, which is possible for small frames (we don't check this), but I don't see why behavior should have changed here from prior versions, and thus I worry that I've missed something. Change-Id: I4b9c4b5ea7adcfb0b82036f8c7068d49fd8b8cb9 Reviewed-on: https://go-review.googlesource.com/c/debug/+/425700 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Keith Randall <khr@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
diff --git a/internal/gocore/gocore_test.go b/internal/gocore/gocore_test.go index 5f8bfd3..ce50b5d 100644 --- a/internal/gocore/gocore_test.go +++ b/internal/gocore/gocore_test.go
@@ -276,6 +276,7 @@ loadExampleVersion(t, "1.14.zip") loadExampleVersion(t, "1.16.zip") loadExampleVersion(t, "1.17.zip") + loadExampleVersion(t, "1.18.zip") } func loadZipCore(t *testing.T, name string) *Process {
diff --git a/internal/gocore/module.go b/internal/gocore/module.go index aacbd0c..f17c181 100644 --- a/internal/gocore/module.go +++ b/internal/gocore/module.go
@@ -46,9 +46,21 @@ n := ftab.SliceLen() - 1 // last slot is a dummy, just holds entry for i := int64(0); i < n; i++ { ft := ftab.SliceIndex(i) - min := core.Address(ft.Field("entry").Uintptr()) - max := core.Address(ftab.SliceIndex(i + 1).Field("entry").Uintptr()) - fr := pcln.SliceIndex(int64(ft.Field("funcoff").Uintptr())).Cast("runtime._func") + var min, max core.Address + var funcoff int64 + if p.minorVersion >= 18 { + min = m.textAddr(ft.Field("entryoff").Uint32()) + max = m.textAddr(ftab.SliceIndex(i + 1).Field("entryoff").Uint32()) + funcoff = int64(ft.Field("funcoff").Uint32()) + } else { + // Prior to 1.18, functab.entry directly referenced the + // entries. + min = core.Address(ft.Field("entry").Uintptr()) + max = core.Address(ftab.SliceIndex(i + 1).Field("entry").Uintptr()) + // funcoff changed type, but had the same meaning. + funcoff = int64(ft.Field("funcoff").Uintptr()) + } + fr := pcln.SliceIndex(funcoff).Cast("runtime._func") var f *Func if p.minorVersion >= 16 { f = m.readFunc(fr, pctab, funcnametab) @@ -69,7 +81,12 @@ // pcln must have type []byte and represent the module's pcln table region. func (m *module) readFunc(r region, pctab region, funcnametab region) *Func { f := &Func{module: m, r: r} - f.entry = core.Address(r.Field("entry").Uintptr()) + if m.p.minorVersion >= 18 { + f.entry = m.textAddr(r.Field("entryoff").Uint32()) + } else { + // Prior to 1.18, _func.entry directly referenced the entries. + f.entry = core.Address(r.Field("entry").Uintptr()) + } f.name = r.p.proc.ReadCString(funcnametab.SliceIndex(int64(r.Field("nameoff").Int32())).a) pcsp := r.Field("pcsp") var pcspIdx int64 @@ -106,8 +123,22 @@ n = uint32(nfd.Int32()) } for i := uint32(0); i < n; i++ { - f.funcdata = append(f.funcdata, r.p.proc.ReadPtr(a)) - a = a.Add(r.p.proc.PtrSize()) + if m.p.minorVersion >= 18 { + // Since 1.18, funcdata contains offsets from go.func.*. + off := r.p.proc.ReadUint32(a) + if off == ^uint32(0) { + // No entry. + f.funcdata = append(f.funcdata, 0) + } else { + f.funcdata = append(f.funcdata, core.Address(m.r.Field("gofunc").Uintptr() + uint64(off))) + } + a = a.Add(4) + } else { + // Prior to 1.18, funcdata contains pointers directly + // to the data. + f.funcdata = append(f.funcdata, r.p.proc.ReadPtr(a)) + a = a.Add(r.p.proc.PtrSize()) + } } // Read pcln tables we need. @@ -120,6 +151,32 @@ return f } +// textAddr returns the address of a text offset. +// +// Equivalent to runtime.moduledata.textAddr. +func (m *module) textAddr(off32 uint32) core.Address { + off := uint64(off32) + res := m.r.Field("text").Uintptr() + off + + textsectmap := m.r.Field("textsectmap") + length := textsectmap.SliceLen() + if length > 1 { + for i := int64(0); i < length; i++ { + sect := textsectmap.SliceIndex(i) + + vaddr := sect.Field("vaddr").Uintptr() + end := sect.Field("end").Uintptr() + baseaddr := sect.Field("baseaddr").Uintptr() + + if off >= vaddr && off < end || (i == length-1 && off == end) { + res = baseaddr + off - vaddr + } + } + } + + return core.Address(res) +} + type funcTabEntry struct { min, max core.Address f *Func
diff --git a/internal/gocore/process.go b/internal/gocore/process.go index c46a731..6bc6960 100644 --- a/internal/gocore/process.go +++ b/internal/gocore/process.go
@@ -678,46 +678,55 @@ // Find live ptrs in locals live := map[core.Address]bool{} if x := int(p.rtConstants["_FUNCDATA_LocalsPointerMaps"]); x < len(f.funcdata) { - locals := region{p: p, a: f.funcdata[x], typ: p.findType("runtime.stackmap")} - n := locals.Field("n").Int32() // # of bitmaps - nbit := locals.Field("nbit").Int32() // # of bits per bitmap - idx, err := f.stackMap.find(off) - if err != nil { - return nil, fmt.Errorf("cannot read stack map at pc=%#x: %v", pc, err) - } - if idx < 0 { - idx = 0 - } - if idx < int64(n) { - bits := locals.Field("bytedata").a.Add(int64(nbit+7) / 8 * idx) - base := frame.max.Add(-16).Add(-int64(nbit) * p.proc.PtrSize()) - // TODO: -16 for amd64. Return address and parent's frame pointer - for i := int64(0); i < int64(nbit); i++ { - if p.proc.ReadUint8(bits.Add(i/8))>>uint(i&7)&1 != 0 { - live[base.Add(i*p.proc.PtrSize())] = true + addr := f.funcdata[x] + // TODO: Ideally we should have the same frame size check as + // runtime.getStackSize to detect errors when we are missing + // the stackmap. + if addr != 0 { + locals := region{p: p, a: addr, typ: p.findType("runtime.stackmap")} + n := locals.Field("n").Int32() // # of bitmaps + nbit := locals.Field("nbit").Int32() // # of bits per bitmap + idx, err := f.stackMap.find(off) + if err != nil { + return nil, fmt.Errorf("cannot read stack map at pc=%#x: %v", pc, err) + } + if idx < 0 { + idx = 0 + } + if idx < int64(n) { + bits := locals.Field("bytedata").a.Add(int64(nbit+7) / 8 * idx) + base := frame.max.Add(-16).Add(-int64(nbit) * p.proc.PtrSize()) + // TODO: -16 for amd64. Return address and parent's frame pointer + for i := int64(0); i < int64(nbit); i++ { + if p.proc.ReadUint8(bits.Add(i/8))>>uint(i&7)&1 != 0 { + live[base.Add(i*p.proc.PtrSize())] = true + } } } } } // Same for args if x := int(p.rtConstants["_FUNCDATA_ArgsPointerMaps"]); x < len(f.funcdata) { - args := region{p: p, a: f.funcdata[x], typ: p.findType("runtime.stackmap")} - n := args.Field("n").Int32() // # of bitmaps - nbit := args.Field("nbit").Int32() // # of bits per bitmap - idx, err := f.stackMap.find(off) - if err != nil { - return nil, fmt.Errorf("cannot read stack map at pc=%#x: %v", pc, err) - } - if idx < 0 { - idx = 0 - } - if idx < int64(n) { - bits := args.Field("bytedata").a.Add(int64(nbit+7) / 8 * idx) - base := frame.max - // TODO: add to base for LR archs. - for i := int64(0); i < int64(nbit); i++ { - if p.proc.ReadUint8(bits.Add(i/8))>>uint(i&7)&1 != 0 { - live[base.Add(i*p.proc.PtrSize())] = true + addr := f.funcdata[x] + if addr != 0 { + args := region{p: p, a: addr, typ: p.findType("runtime.stackmap")} + n := args.Field("n").Int32() // # of bitmaps + nbit := args.Field("nbit").Int32() // # of bits per bitmap + idx, err := f.stackMap.find(off) + if err != nil { + return nil, fmt.Errorf("cannot read stack map at pc=%#x: %v", pc, err) + } + if idx < 0 { + idx = 0 + } + if idx < int64(n) { + bits := args.Field("bytedata").a.Add(int64(nbit+7) / 8 * idx) + base := frame.max + // TODO: add to base for LR archs. + for i := int64(0); i < int64(nbit); i++ { + if p.proc.ReadUint8(bits.Add(i/8))>>uint(i&7)&1 != 0 { + live[base.Add(i*p.proc.PtrSize())] = true + } } } }
diff --git a/internal/gocore/testdata/1.18.zip b/internal/gocore/testdata/1.18.zip new file mode 100644 index 0000000..105a586 --- /dev/null +++ b/internal/gocore/testdata/1.18.zip Binary files differ