Skip to content

Commit eabef37

Browse files
committed
Convert runtime regions to use full-fledged Type structures
instead of hacky types-as-strings. Reorg the passes a bit so there aren't so many runtime-specific data structures.
1 parent 9b149ed commit eabef37

File tree

5 files changed

+122
-208
lines changed

5 files changed

+122
-208
lines changed

core/read.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func (p *Process) readCore(core *os.File) error {
7777
return err
7878
}
7979
if e.Type != elf.ET_CORE {
80-
return fmt.Errorf("%s is not a core file", core)
80+
return fmt.Errorf("%s is not a core file", core.Name())
8181
}
8282
switch e.Class {
8383
case elf.ELFCLASS32:

gocore/dwarf.go

Lines changed: 31 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ func (p *Program) readDWARFTypes() {
1515

1616
// Make one of our own Types for each dwarf type.
1717
r := d.Reader()
18+
var types []*Type
1819
for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() {
1920
switch e.Tag {
2021
case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef:
@@ -27,8 +28,8 @@ func (p *Program) readDWARFTypes() {
2728
size = dwarfSize(dt, p.proc.PtrSize())
2829
}
2930
t := &Type{name: gocoreName(dt), Size: size}
30-
p.types = append(p.types, t)
3131
p.dwarfMap[dt] = t
32+
types = append(types, t)
3233
}
3334
}
3435

@@ -71,7 +72,7 @@ func (p *Program) readDWARFTypes() {
7172
}
7273

7374
// Detect strings & slices
74-
for _, t := range p.types {
75+
for _, t := range types {
7576
if t.Kind != KindStruct {
7677
continue
7778
}
@@ -132,6 +133,13 @@ func (p *Program) readDWARFTypes() {
132133
t.Fields = nil
133134
}
134135
}
136+
137+
// Make a runtime name -> Type map for existing DWARF types.
138+
p.runtimeNameMap = map[string][]*Type{}
139+
for dt, t := range p.dwarfMap {
140+
name := runtimeName(dt)
141+
p.runtimeNameMap[name] = append(p.runtimeNameMap[name], t)
142+
}
135143
}
136144

137145
// dwarfSize is used to compute the size of a DWARF type when .Size()
@@ -456,12 +464,12 @@ func (p *Program) typeObject(a core.Address, t *Type, r reader, add func(core.Ad
456464
}
457465
ptr := r.ReadPtr(a.Add(ptrSize))
458466
if t.Kind == KindIface {
459-
typ = p.proc.ReadPtr(typ.Add(p.rtStructs["runtime.itab"].fields["_type"].off))
467+
typ = p.proc.ReadPtr(typ.Add(p.findType("runtime.itab").field("_type").Off))
460468
}
461469
// TODO: for KindEface, type the typ pointer. It might point to the heap
462470
// if the type was allocated with reflect.
463471

464-
direct := p.proc.ReadUint8(typ.Add(p.rtStructs["runtime._type"].fields["kind"].off))&uint8(p.rtConstants["kindDirectIface"]) != 0
472+
direct := p.proc.ReadUint8(typ.Add(p.findType("runtime._type").field("kind").Off))&uint8(p.rtConstants["kindDirectIface"]) != 0
465473
dt := p.runtimeType2Type(typ)
466474
if direct {
467475
// Find the base type of the pointer held in the interface.
@@ -514,7 +522,6 @@ func (p *Program) typeObject(a core.Address, t *Type, r reader, add func(core.Ad
514522
ft := f.closure
515523
if ft == nil {
516524
ft = &Type{name: "closure for " + f.name, Size: ptrSize, Kind: KindPtr}
517-
p.types = append(p.types, ft)
518525
// For now, treat a closure like an unsafe.Pointer.
519526
// TODO: better value for size?
520527
f.closure = ft
@@ -553,27 +560,8 @@ func (p *Program) typeObject(a core.Address, t *Type, r reader, add func(core.Ad
553560
}
554561
}
555562

556-
// findRuntimeInfo uses DWARF information to find all the struct sizes,
557-
// field offsets, and field types for runtime data structures.
558-
// It also finds a useful set of runtime constants.
559-
// It populates p.rtStructs and rtConstants.
560-
func (p *Program) findRuntimeInfo() {
561-
p.rtStructs = map[string]structInfo{}
562-
for dt, _ := range p.dwarfMap {
563-
rtname := runtimeName(dt)
564-
if !strings.HasPrefix(rtname, "runtime.") {
565-
continue
566-
}
567-
x, ok := dt.(*dwarf.StructType)
568-
if !ok {
569-
continue
570-
}
571-
s := structInfo{size: dt.Size(), fields: map[string]fieldInfo{}}
572-
for _, f := range x.Field {
573-
s.fields[f.Name] = fieldInfo{off: f.ByteOffset, typ: runtimeName(f.Type)}
574-
}
575-
p.rtStructs[rtname] = s
576-
}
563+
// readRuntimeConstants populates the p.rtConstants map.
564+
func (p *Program) readRuntimeConstants() {
577565
p.rtConstants = map[string]int64{}
578566
// TODO: It would be ideal if we could glean this info from DWARF.
579567
// But we don't package up constants in DWARF right now. See issue #14517.
@@ -600,23 +588,22 @@ func (p *Program) findRuntimeInfo() {
600588
m["_PageSize"] = 1 << 13
601589
}
602590

603-
func (p *Program) findRoots() {
604-
const (
605-
DW_OP_addr = 0x03
606-
DW_OP_call_frame_cfa = 0x9c
607-
DW_OP_plus = 0x22
608-
DW_OP_consts = 0x11
609-
)
610-
d, _ := p.proc.DWARF()
591+
const (
592+
_DW_OP_addr = 0x03
593+
_DW_OP_call_frame_cfa = 0x9c
594+
_DW_OP_plus = 0x22
595+
_DW_OP_consts = 0x11
596+
)
611597

612-
// Find global variables.
598+
func (p *Program) readGlobals() {
599+
d, _ := p.proc.DWARF()
613600
r := d.Reader()
614601
for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() {
615602
if e.Tag != dwarf.TagVariable {
616603
continue
617604
}
618605
loc := e.AttrField(dwarf.AttrLocation).Val.([]byte)
619-
if loc[0] != DW_OP_addr {
606+
if loc[0] != _DW_OP_addr {
620607
continue
621608
}
622609
var a core.Address
@@ -644,16 +631,18 @@ func (p *Program) findRoots() {
644631
Live: nil,
645632
})
646633
}
634+
}
647635

648-
// Find stack variables.
636+
func (p *Program) readStackVars() {
649637
type Var struct {
650638
name string
651639
off int64
652640
typ *Type
653641
}
654642
vars := map[*Func][]Var{}
655643
var curfn *Func
656-
r = d.Reader()
644+
d, _ := p.proc.DWARF()
645+
r := d.Reader()
657646
for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() {
658647
if e.Tag == dwarf.TagSubprogram {
659648
min := core.Address(e.AttrField(dwarf.AttrLowpc).Val.(uint64))
@@ -681,14 +670,14 @@ func (p *Program) findRoots() {
681670
continue
682671
}
683672
loc := aloc.Val.([]byte)
684-
if len(loc) == 0 || loc[0] != DW_OP_call_frame_cfa {
673+
if len(loc) == 0 || loc[0] != _DW_OP_call_frame_cfa {
685674
continue
686675
}
687676
loc = loc[1:]
688677
var off int64
689-
if len(loc) != 0 && loc[len(loc)-1] == DW_OP_plus {
678+
if len(loc) != 0 && loc[len(loc)-1] == _DW_OP_plus {
690679
loc = loc[:len(loc)-1]
691-
if len(loc) == 0 || loc[0] != DW_OP_consts {
680+
if len(loc) == 0 || loc[0] != _DW_OP_consts {
692681
continue
693682
}
694683
loc = loc[1:]
@@ -748,7 +737,7 @@ func (p *Program) findRoots() {
748737
r := &Root{
749738
Name: "unk",
750739
Addr: a,
751-
Type: p.runtimeNameMap["unsafe.Pointer"][0],
740+
Type: p.findType("unsafe.Pointer"),
752741
Live: f.live,
753742
}
754743
f.roots = append(f.roots, r)

gocore/parse.go

Lines changed: 29 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,13 @@ package gocore
33
import (
44
"debug/dwarf"
55
"fmt"
6+
"strings"
67

78
"github.com/randall77/corelib/core"
89
)
910

1011
// Core takes a loaded core file and extracts Go information from it.
1112
func Core(proc *core.Process) (p *Program, err error) {
12-
// Check symbol table to make sure we know the addresses
13-
// of some critical runtime data structures.
14-
m, err := proc.Symbols()
15-
if err != nil {
16-
return nil, err
17-
}
18-
for _, s := range rtSymbols {
19-
if m["runtime."+s.name] == 0 {
20-
// We're missing some address that we need.
21-
return nil, fmt.Errorf("can't find runtime data structure %s. Is the binary unstripped?", s.name)
22-
}
23-
}
24-
// TODO: is the symbol table redundant with the DWARF info? Could we just use DWARF?
25-
2613
// Make sure we have DWARF info.
2714
if _, err := proc.DWARF(); err != nil {
2815
return nil, err
@@ -50,51 +37,34 @@ func Core(proc *core.Process) (p *Program, err error) {
5037
dwarfMap: map[dwarf.Type]*Type{},
5138
}
5239

53-
// Load the build version.
54-
a := m["runtime.buildVersion"]
55-
ptr := proc.ReadPtr(a)
56-
len := proc.ReadInt(a.Add(proc.PtrSize()))
57-
b := make([]byte, len)
58-
proc.ReadAt(b, ptr)
59-
p.buildVersion = string(b)
60-
61-
// Initialize runtime regions.
62-
p.runtime = map[string]region{}
63-
for _, s := range rtSymbols {
64-
p.runtime[s.name] = region{p: p, a: m["runtime."+s.name], typ: s.typ}
40+
// Initialize everything that just depends on DWARF.
41+
p.readDWARFTypes()
42+
p.readRuntimeConstants()
43+
p.readGlobals()
44+
45+
// Find runtime globals we care about. Initialize regions for them.
46+
p.rtGlobals = map[string]region{}
47+
for _, g := range p.globals {
48+
if strings.HasPrefix(g.Name, "runtime.") {
49+
p.rtGlobals[g.Name[8:]] = region{p: p, a: g.Addr, typ: g.Type}
50+
}
6551
}
6652

67-
p.readDWARFTypes()
68-
p.findRuntimeInfo()
53+
// Read all the data that depends on runtime globals.
54+
p.buildVersion = p.rtGlobals["buildVersion"].String()
6955
p.readModules()
56+
p.readStackVars()
7057
p.readSpans()
7158
p.readMs()
7259
p.readGs()
73-
p.findRoots()
7460
p.readObjects()
7561
p.typeHeap()
76-
return
77-
}
7862

79-
// rtSymbols is a list of all the runtime globals that we need to access,
80-
// together with their types.
81-
var rtSymbols = [...]struct {
82-
name, typ string
83-
}{
84-
{"mheap_", "runtime.mheap"},
85-
{"memstats", "runtime.mstats"},
86-
{"sched", "runtime.schedt"},
87-
{"allfin", "*runtime.finblock"},
88-
{"finq", "*runtime.finblock"},
89-
{"allgs", "[]*runtime.g"},
90-
{"allm", "*runtime.m"},
91-
{"allp", "[1]*p"}, // TODO: type depends on _MaxGomaxprocs
92-
{"modulesSlice", "*[]*runtime.moduledata"},
93-
{"buildVersion", "string"},
63+
return p, nil
9464
}
9565

9666
func (p *Program) readSpans() {
97-
mheap := p.runtime["mheap_"]
67+
mheap := p.rtGlobals["mheap_"]
9868

9969
spanTableStart := mheap.Field("spans").SlicePtr().Address()
10070
spanTableEnd := spanTableStart.Add(mheap.Field("spans").SliceCap() * p.proc.PtrSize())
@@ -260,14 +230,7 @@ func (p *Program) readSpans() {
260230
}
261231

262232
func (p *Program) readModules() {
263-
// Make a runtime name -> Type map for existing DWARF types.
264-
p.runtimeNameMap = map[string][]*Type{}
265-
for dt, t := range p.dwarfMap {
266-
name := runtimeName(dt)
267-
p.runtimeNameMap[name] = append(p.runtimeNameMap[name], t)
268-
}
269-
270-
ms := p.runtime["modulesSlice"].Deref()
233+
ms := p.rtGlobals["modulesSlice"].Cast("*[]*runtime.moduledata").Deref()
271234
n := ms.SliceLen()
272235
for i := int64(0); i < n; i++ {
273236
md := ms.SliceIndex(i).Deref()
@@ -343,13 +306,13 @@ func (t *Type) ptrs1(s []int64, off int64) []int64 {
343306
// Convert the address of a runtime._type to a *Type.
344307
// Guaranteed to return a non-nil *Type.
345308
func (p *Program) runtimeType2Type(a core.Address) *Type {
346-
if t, ok := p.runtimeMap[a]; ok {
309+
if t := p.runtimeMap[a]; t != nil {
347310
return t
348311
}
349312
ptrSize := p.proc.PtrSize()
350313

351314
// Read runtime._type.size
352-
r := region{p: p, a: a, typ: "runtime._type"}
315+
r := region{p: p, a: a, typ: p.findType("runtime._type")}
353316
size := int64(r.Field("size").Uintptr())
354317

355318
// Find module this type is in.
@@ -413,13 +376,12 @@ func (p *Program) runtimeType2Type(a core.Address) *Type {
413376
} else {
414377
// There's no corresponding DWARF type. Make our own.
415378
t = &Type{name: name, Size: size, Kind: KindStruct}
416-
p.types = append(p.types, t)
417379
n := t.Size / ptrSize
418380

419381
// Types to use for ptr/nonptr fields of runtime types which
420382
// have no corresponding DWARF type.
421-
ptr := p.runtimeNameMap["unsafe.Pointer"][0]
422-
nonptr := p.runtimeNameMap["uintptr"][0]
383+
ptr := p.findType("unsafe.Pointer")
384+
nonptr := p.findType("uintptr")
423385
if ptr == nil || nonptr == nil {
424386
panic("ptr / nonptr standins missing")
425387
}
@@ -457,7 +419,7 @@ func (m *module) readFunc(r region, pcln region) *Func {
457419
f.frameSize.read(r.p.proc, pcln.SliceIndex(int64(r.Field("pcsp").Int32())).a)
458420

459421
// Parse pcdata and funcdata, which are laid out beyond the end of the _func.
460-
a := r.a.Add(int64(r.p.rtStructs["runtime._func"].size))
422+
a := r.a.Add(int64(r.p.findType("runtime._func").Size))
461423
n := r.Field("npcdata").Int32()
462424
for i := int32(0); i < n; i++ {
463425
f.pcdata = append(f.pcdata, r.p.proc.ReadInt32(a))
@@ -485,7 +447,7 @@ func pcdata(r region, pcln region, n int64) core.Address {
485447
if n >= int64(r.Field("npcdata").Int32()) {
486448
return 0
487449
}
488-
a := r.a.Add(int64(r.p.rtStructs["runtime._func"].size + 4*n))
450+
a := r.a.Add(int64(r.p.findType("runtime._func").Size + 4*n))
489451

490452
off := r.p.proc.ReadInt32(a)
491453
return pcln.SliceIndex(int64(off)).a
@@ -501,12 +463,12 @@ func funcdata(r region, n int64) core.Address {
501463
if x&1 != 0 && r.p.proc.PtrSize() == 8 {
502464
x++
503465
}
504-
a := r.a.Add(int64(r.p.rtStructs["runtime._func"].size + 4*int64(x) + r.p.proc.PtrSize()*n))
466+
a := r.a.Add(int64(r.p.findType("runtime._func").Size + 4*int64(x) + r.p.proc.PtrSize()*n))
505467
return r.p.proc.ReadPtr(a)
506468
}
507469

508470
func (p *Program) readMs() {
509-
mp := p.runtime["allm"]
471+
mp := p.rtGlobals["allm"]
510472
for mp.Address() != 0 {
511473
m := mp.Deref()
512474
gs := m.Field("gsignal")
@@ -521,7 +483,7 @@ func (p *Program) readMs() {
521483

522484
func (p *Program) readGs() {
523485
// TODO: figure out how to "flush" running Gs.
524-
allgs := p.runtime["allgs"]
486+
allgs := p.rtGlobals["allgs"]
525487
n := allgs.SliceLen()
526488
for i := int64(0); i < n; i++ {
527489
r := allgs.SliceIndex(i).Deref()
@@ -625,7 +587,7 @@ func (p *Program) readFrame(sp, pc core.Address) *Frame {
625587
// Find live ptrs in locals
626588
live := map[core.Address]bool{}
627589
if x := int(p.rtConstants["_FUNCDATA_LocalsPointerMaps"]); x < len(f.funcdata) {
628-
locals := region{p: p, a: f.funcdata[x], typ: "runtime.stackmap"}
590+
locals := region{p: p, a: f.funcdata[x], typ: p.findType("runtime.stackmap")}
629591
n := locals.Field("n").Int32() // # of bitmaps
630592
nbit := locals.Field("nbit").Int32() // # of bits per bitmap
631593
idx := f.stackMap.find(off)
@@ -645,7 +607,7 @@ func (p *Program) readFrame(sp, pc core.Address) *Frame {
645607
}
646608
// Same for args
647609
if x := int(p.rtConstants["_FUNCDATA_ArgsPointerMaps"]); x < len(f.funcdata) {
648-
args := region{p: p, a: f.funcdata[x], typ: "runtime.stackmap"}
610+
args := region{p: p, a: f.funcdata[x], typ: p.findType("runtime.stackmap")}
649611
n := args.Field("n").Int32() // # of bitmaps
650612
nbit := args.Field("nbit").Int32() // # of bits per bitmap
651613
idx := f.stackMap.find(off)

0 commit comments

Comments
 (0)