Skip to content

Commit

Permalink
gocore: more fixes for Go 1.24
Browse files Browse the repository at this point in the history
- The isCrashFrame flag during unwinding was never reset, which could
  be bad.
- We're apparently reading a lot of stale constants and not seeing it
  because a failed read from the constants map just returns the zero
  value. Oops! Add a much stricter map wrapper type for constants and
  fix up all the stale constants. This fixes a whole bunch of things,
  including totally incorrect readings of the liveness maps.
- More strict checking of liveness map data.

Change-Id: I2cb4901df4e3f473ef8b0c09981ba222e883b0b6
Reviewed-on: https://go-review.googlesource.com/c/debug/+/635835
Reviewed-by: Michael Pratt <[email protected]>
Auto-Submit: Nicolas Hillegeer <[email protected]>
Commit-Queue: Nicolas Hillegeer <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Nicolas Hillegeer <[email protected]>
  • Loading branch information
mknyszek authored and gopherbot committed Dec 13, 2024
1 parent 96bf9a9 commit 46b335e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 42 deletions.
22 changes: 16 additions & 6 deletions internal/gocore/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,14 +241,28 @@ func gocoreName(dt dwarf.Type) string {
}
}

func readRuntimeConstants(p *core.Process) (map[string]int64, error) {
type constsMap map[string]int64

func (c constsMap) get(s string) int64 {
v, ok := c[s]
if !ok {
panic("failed to find constant " + s)
}
return v
}

func (c constsMap) find(s string) (int64, bool) {
v, ok := c[s]
return v, ok
}

func readConstants(p *core.Process) (constsMap, error) {
d, err := p.DWARF()
if err != nil {
return nil, fmt.Errorf("failed to read DWARF: %v", err)
}
consts := map[string]int64{}

// From 1.10, these constants are recorded in DWARF records.
r := d.Reader()
for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() {
if e.Tag != dwarf.TagConstant {
Expand All @@ -259,10 +273,6 @@ func readRuntimeConstants(p *core.Process) (map[string]int64, error) {
continue
}
name := f.Val.(string)
if !strings.HasPrefix(name, "runtime.") {
continue
}
name = name[8:]
c := e.AttrField(dwarf.AttrConstValue)
if c == nil {
continue
Expand Down
4 changes: 2 additions & 2 deletions internal/gocore/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func readModule(r region, fns *funcTab, rtTypeByName map[string]*Type, rtConsts
// readFunc parses a runtime._func and returns a *Func.
// r must have type runtime._func.
// pcln must have type []byte and represent the module's pcln table region.
func (m *module) readFunc(r region, pctab region, funcnametab region, rtConsts map[string]int64) *Func {
func (m *module) readFunc(r region, pctab region, funcnametab region, rtConsts constsMap) *Func {
f := &Func{module: m, r: r}
f.entry = m.textAddr(r.Field("entryOff").Uint32())
nameOff := r.Field("nameOff").Int32()
Expand Down Expand Up @@ -103,7 +103,7 @@ func (m *module) readFunc(r region, pctab region, funcnametab region, rtConsts m
}

// Read pcln tables we need.
if stackmap := int(rtConsts["_PCDATA_StackMapIndex"]); stackmap < len(f.pcdata) {
if stackmap := int(rtConsts.get("internal/abi.PCDATA_StackMapIndex")); stackmap < len(f.pcdata) {
f.stackMap.read(r.p, pctab.SliceIndex(int64(f.pcdata[stackmap])).a)
} else {
f.stackMap.setEmpty()
Expand Down
66 changes: 38 additions & 28 deletions internal/gocore/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type Process struct {

// Runtime info for easier lookup.
rtGlobals map[string]region
rtConsts map[string]int64
rtConsts constsMap

// A module is a loadable unit. Most Go programs have 1, programs
// which load plugins will have more.
Expand Down Expand Up @@ -98,7 +98,7 @@ func Core(proc *core.Process) (p *Process, err error) {
}
p.rtTypeByName[name] = t
}
p.rtConsts, err = readRuntimeConstants(proc)
p.rtConsts, err = readConstants(proc)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -201,11 +201,11 @@ func readHeap(p *Process) (*heapTable, *Statistic, error) {
mheap := p.rtGlobals["mheap_"]

var arenas []arena
arenaSize := p.rtConsts["heapArenaBytes"]
arenaSize := p.rtConsts.get("runtime.heapArenaBytes")
if arenaSize%heapInfoSize != 0 {
panic("arenaSize not a multiple of heapInfoSize")
}
arenaBaseOffset := -p.rtConsts["arenaBaseOffsetUintptr"]
arenaBaseOffset := -p.rtConsts.get("runtime.arenaBaseOffsetUintptr")
if p.proc.PtrSize() == 4 && arenaBaseOffset != 0 {
panic("arenaBaseOffset must be 0 for 32-bit inferior")
}
Expand Down Expand Up @@ -312,17 +312,17 @@ func readHeap0(p *Process, mheap region, arenas []arena, arenaBaseOffset int64)
return nil, nil, errors.New("weird mapping " + m.Perm().String())
}
}
pageSize := p.rtConsts["_PageSize"]
pageSize := p.rtConsts.get("runtime._PageSize")

// Span types.
spanInUse := uint8(p.rtConsts["mSpanInUse"])
spanManual := uint8(p.rtConsts["mSpanManual"])
spanDead := uint8(p.rtConsts["mSpanDead"])
spanInUse := uint8(p.rtConsts.get("runtime.mSpanInUse"))
spanManual := uint8(p.rtConsts.get("runtime.mSpanManual"))
spanDead := uint8(p.rtConsts.get("runtime.mSpanDead"))

// Malloc header constants (go 1.22+)
minSizeForMallocHeader := int64(p.rtConsts["minSizeForMallocHeader"])
mallocHeaderSize := int64(p.rtConsts["mallocHeaderSize"])
maxSmallSize := int64(p.rtConsts["maxSmallSize"])
minSizeForMallocHeader := int64(p.rtConsts.get("runtime.minSizeForMallocHeader"))
mallocHeaderSize := int64(p.rtConsts.get("runtime.mallocHeaderSize"))
maxSmallSize := int64(p.rtConsts.get("runtime.maxSmallSize"))

abiType := p.tryFindType("internal/abi.Type")

Expand Down Expand Up @@ -402,7 +402,7 @@ func readHeap0(p *Process, mheap region, arenas []arena, arenaBaseOffset int64)
// Process special records.
for sp := s.Field("specials"); sp.Address() != 0; sp = sp.Field("next") {
sp = sp.Deref() // *special to special
if sp.Field("kind").Uint8() != uint8(p.rtConsts["_KindSpecialFinalizer"]) {
if sp.Field("kind").Uint8() != uint8(p.rtConsts.get("runtime._KindSpecialFinalizer")) {
// All other specials (just profile records) can't point into the heap.
continue
}
Expand Down Expand Up @@ -466,7 +466,8 @@ func readHeap0(p *Process, mheap region, arenas []arena, arenaBaseOffset int64)
}
typ := region{p: p.proc, a: typeAddr, typ: abiType}
nptrs := int64(typ.Field("PtrBytes").Uintptr()) / int64(heap.ptrSize)
if typ.Field("Kind_").Uint8()&uint8(p.rtConsts["kindGCProg"]) != 0 {
kindGCProg, hasGCProgs := p.rtConsts.find("internal/abi.KindGCProg")
if hasGCProgs && typ.Field("Kind_").Uint8()&uint8(kindGCProg) != 0 {
panic("unexpected GC prog on small allocation")
}
gcdata := typ.Field("GCData").Address()
Expand All @@ -485,7 +486,8 @@ func readHeap0(p *Process, mheap region, arenas []arena, arenaBaseOffset int64)
// is in use.
typ := s.Field("largeType").Deref()
nptrs := int64(typ.Field("PtrBytes").Uintptr()) / int64(heap.ptrSize)
if typ.Field("Kind_").Uint8()&uint8(p.rtConsts["kindGCProg"]) != 0 {
kindGCProg, hasGCProgs := p.rtConsts.find("internal/abi.KindGCProg")
if hasGCProgs && typ.Field("Kind_").Uint8()&uint8(kindGCProg) != 0 {
panic("large object's GCProg was not unrolled")
}
gcdata := typ.Field("GCData").Address()
Expand Down Expand Up @@ -513,9 +515,9 @@ func readHeap0(p *Process, mheap region, arenas []arena, arenaBaseOffset int64)
// Also keep track of how much has been scavenged.
pages := mheap.Field("pages")
chunks := pages.Field("chunks")
pallocChunkBytes := p.rtConsts["pallocChunkBytes"]
pallocChunksL1Bits := p.rtConsts["pallocChunksL1Bits"]
pallocChunksL2Bits := p.rtConsts["pallocChunksL2Bits"]
pallocChunkBytes := p.rtConsts.get("runtime.pallocChunkBytes")
pallocChunksL1Bits := p.rtConsts.get("runtime.pallocChunksL1Bits")
pallocChunksL2Bits := p.rtConsts.get("runtime.pallocChunksL2Bits")
inuse := pages.Field("inUse")
ranges := inuse.Field("ranges")
for i := int64(0); i < ranges.SliceLen(); i++ {
Expand Down Expand Up @@ -621,24 +623,24 @@ func readGoroutine(p *Process, r region, dwarfVars map[*Func][]dwarfVar) (*Gorou
}
st := r.Field("atomicstatus").Field("value")
status := st.Uint32()
status &^= uint32(p.rtConsts["_Gscan"])
status &^= uint32(p.rtConsts.get("runtime._Gscan"))
var sp, pc core.Address
switch status {
case uint32(p.rtConsts["_Gidle"]):
case uint32(p.rtConsts.get("runtime._Gidle")):
return g, nil
case uint32(p.rtConsts["_Grunnable"]), uint32(p.rtConsts["_Gwaiting"]):
case uint32(p.rtConsts.get("runtime._Grunnable")), uint32(p.rtConsts.get("runtime._Gwaiting")):
sched := r.Field("sched")
sp = core.Address(sched.Field("sp").Uintptr())
pc = core.Address(sched.Field("pc").Uintptr())
case uint32(p.rtConsts["_Grunning"]):
case uint32(p.rtConsts.get("runtime._Grunning")):
sp = osT.SP()
pc = osT.PC()
// TODO: back up to the calling frame?
case uint32(p.rtConsts["_Gsyscall"]):
case uint32(p.rtConsts.get("runtime._Gsyscall")):
sp = core.Address(r.Field("syscallsp").Uintptr())
pc = core.Address(r.Field("syscallpc").Uintptr())
// TODO: or should we use the osT registers?
case uint32(p.rtConsts["_Gdead"]):
case uint32(p.rtConsts.get("runtime._Gdead")):
return nil, nil
// TODO: copystack, others?
default:
Expand Down Expand Up @@ -874,6 +876,8 @@ func readGoroutine(p *Process, r region, dwarfVars map[*Func][]dwarfVar) (*Gorou
} else {
sp = f.max
pc = core.Address(p.proc.ReadUintptr(sp - 8)) // TODO:amd64 only

isCrashFrame = false
}
if pc == 0 {
// TODO: when would this happen?
Expand Down Expand Up @@ -905,7 +909,7 @@ func readFrame(p *Process, sp, pc core.Address) (*Frame, error) {

// Find live ptrs in locals
live := map[core.Address]bool{}
if x := int(p.rtConsts["_FUNCDATA_LocalsPointerMaps"]); x < len(f.funcdata) {
if x := int(p.rtConsts.get("internal/abi.FUNCDATA_LocalsPointerMaps")); x < len(f.funcdata) {
addr := f.funcdata[x]
// TODO: Ideally we should have the same frame size check as
// runtime.getStackSize to detect errors when we are missing
Expand All @@ -918,7 +922,10 @@ func readFrame(p *Process, sp, pc core.Address) (*Frame, error) {
if err != nil {
return nil, fmt.Errorf("cannot read stack map at pc=%#x: %v", pc, err)
}
if idx < 0 {
if idx < -1 {
return nil, fmt.Errorf("cannot read stack map at pc=%#x: invalid stack map index %d", pc, idx)
}
if idx == -1 {
idx = 0
}
if idx < int64(n) {
Expand All @@ -934,7 +941,7 @@ func readFrame(p *Process, sp, pc core.Address) (*Frame, error) {
}
}
// Same for args
if x := int(p.rtConsts["_FUNCDATA_ArgsPointerMaps"]); x < len(f.funcdata) {
if x := int(p.rtConsts.get("internal/abi.FUNCDATA_ArgsPointerMaps")); x < len(f.funcdata) {
addr := f.funcdata[x]
if addr != 0 {
args := region{p: p.proc, a: addr, typ: p.findType("runtime.stackmap")}
Expand All @@ -944,11 +951,14 @@ func readFrame(p *Process, sp, pc core.Address) (*Frame, error) {
if err != nil {
return nil, fmt.Errorf("cannot read stack map at pc=%#x: %v", pc, err)
}
if idx < 0 {
if idx < -1 {
return nil, fmt.Errorf("cannot read stack map at pc=%#x: invalid stack map index %d", pc, idx)
}
if idx == -1 {
idx = 0
}
if idx < int64(n) {
bits := args.Field("bytedata").a.Add(int64(nbit+7) / 8 * idx)
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++ {
Expand Down
13 changes: 7 additions & 6 deletions internal/gocore/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func (p *Process) runtimeType2Type(a core.Address, d core.Address) *Type {
b := make([]byte, n)
p.proc.ReadAt(b, x.Add(i+1))
name = string(b)
if r.TFlag()&uint8(p.rtConsts["tflagExtraStar"]) != 0 {
if r.TFlag()&uint8(p.rtConsts.get("internal/abi.TFlagExtraStar")) != 0 {
name = name[1:]
}
} else {
Expand All @@ -238,15 +238,16 @@ func (p *Process) runtimeType2Type(a core.Address, d core.Address) *Type {
ptrSize := p.proc.PtrSize()
nptrs := int64(r.PtrBytes()) / ptrSize
var ptrs []int64
if r.Kind_()&uint8(p.rtConsts["kindGCProg"]) == 0 {
kindGCProg, hasGCProgs := p.rtConsts.find("internal/abi.KindGCProg")
if hasGCProgs && r.Kind_()&uint8(kindGCProg) != 0 {
// TODO: run GC program. Go 1.23 and earlier only.
} else {
gcdata := r.GCData()
for i := int64(0); i < nptrs; i++ {
if p.proc.ReadUint8(gcdata.Add(i/8))>>uint(i%8)&1 != 0 {
ptrs = append(ptrs, i*ptrSize)
}
}
} else {
// TODO: run GC program to get ptr indexes
}

// Find a Type that matches this type.
Expand Down Expand Up @@ -540,7 +541,7 @@ func (p *Process) doTypeHeap() {
p.typeObject(r.Addr, r.Type, fr, add)
} else if r.Addr == 0 && r.Type.Kind == KindPtr && r.Type.Elem != nil {
p.typeObject(r.RegValue, r.Type.Elem, fr, add)
} else {
} else if r.Addr != 0 {
p.typeObject(r.Addr, r.Type, p.proc, add)
}
return true
Expand Down Expand Up @@ -631,7 +632,7 @@ func extractTypeFromFunctionName(method string, p *Process) *Type {
// ifaceIndir reports whether t is stored indirectly in an interface value.
func ifaceIndir(t core.Address, p *Process) bool {
typr := p.findRuntimeType(t)
return typr.Kind_()&uint8(p.rtConsts["kindDirectIface"]) == 0
return typr.Kind_()&uint8(p.rtConsts.get("internal/abi.KindDirectIface")) == 0
}

// typeObject takes an address and a type for the data at that address.
Expand Down

0 comments on commit 46b335e

Please sign in to comment.