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