Skip to content

Commit 84fb7c8

Browse files
thanmmknyszek
authored andcommitted
internal/gocore: support for DWARF version 5 location lists
Rework the DWARF reading code to handle location lists in DWARF 5 format in addition to the existing DWARF 2/4 code. Updates golang/go#26379. Change-Id: I9d502f5bcecc752e4c1fe7cfaa9ff5871fe417de Reviewed-on: https://go-review.googlesource.com/c/debug/+/654256 Reviewed-by: Junyang Shao <shaojunyang@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
1 parent 8850c75 commit 84fb7c8

File tree

2 files changed

+144
-47
lines changed

2 files changed

+144
-47
lines changed

internal/core/process.go

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,13 @@ type Process struct {
4747
memory splicedMemory // virtual address mappings
4848
pageTable pageTable4 // for fast address->mapping lookups
4949

50-
syms map[string]Address // symbols (could be empty if executable is stripped)
51-
symErr error // an error encountered while reading symbols
52-
dwarf *dwarf.Data // debugging info (could be nil)
53-
dwarfErr error // an error encountered while reading DWARF
54-
dwarfLoc []byte // .debug_loc section
50+
syms map[string]Address // symbols (could be empty if executable is stripped)
51+
symErr error // an error encountered while reading symbols
52+
dwarf *dwarf.Data // debugging info (could be nil)
53+
dwarfErr error // an error encountered while reading DWARF
54+
dwarfLoc []byte // .debug_loc section
55+
dwarfLocLists []byte // .debug_loclists section
56+
dwarfAddr []byte // .debug_addr section
5557

5658
warnings []string // warnings generated during loading
5759
}
@@ -181,6 +183,14 @@ func (p *Process) DWARFLoc() ([]byte, error) {
181183
return p.dwarfLoc, p.dwarfErr
182184
}
183185

186+
func (p *Process) DWARFLocLists() ([]byte, error) {
187+
return p.dwarfLocLists, p.dwarfErr
188+
}
189+
190+
func (p *Process) DWARFAddr() ([]byte, error) {
191+
return p.dwarfAddr, p.dwarfErr
192+
}
193+
184194
// Symbols returns a mapping from name to inferior address, along with
185195
// any error encountered during reading the symbol information.
186196
// (There may be both an error and some returned symbols.)
@@ -282,12 +292,27 @@ func Core(corePath, base, exePath string) (*Process, error) {
282292
if dwarfErr != nil {
283293
dwarfErr = fmt.Errorf("error reading DWARF info from %s: %v", exeFile.Name(), dwarfErr)
284294
}
285-
var dwarfLoc []byte
286-
if locSection := exeElf.Section(".debug_loc"); locSection != nil {
287-
var err error
288-
dwarfLoc, err = locSection.Data()
289-
if err != nil && dwarfErr == nil {
290-
dwarfErr = fmt.Errorf("error reading DWARF location list section from %s: %v", exeFile.Name(), err)
295+
296+
// Note that we expect to find .debug_loc for DWARF V4 binaries
297+
// and .debug_loclists + .debug_addr for DWARF V5. If C code is
298+
// mixed in, we may see both sections, since you're allowed to mix
299+
// different DWARF versions by compilation unit.
300+
var dwarfLoc, dwarfLocLists, dwarfAddr []byte
301+
toRead := []struct {
302+
name string
303+
payload *[]byte
304+
}{
305+
{name: ".debug_loc", payload: &dwarfLoc},
306+
{name: ".debug_loclists", payload: &dwarfLocLists},
307+
{name: ".debug_addr", payload: &dwarfAddr},
308+
}
309+
for _, secitem := range toRead {
310+
if section := exeElf.Section(secitem.name); section != nil {
311+
payload, err := section.Data()
312+
if err != nil && dwarfErr == nil {
313+
dwarfErr = fmt.Errorf("error reading DWARF %s section from %s: %v", secitem.name, exeFile.Name(), err)
314+
}
315+
*(secitem.payload) = payload
291316
}
292317
}
293318

@@ -368,19 +393,21 @@ func Core(corePath, base, exePath string) (*Process, error) {
368393
}
369394

370395
p := &Process{
371-
meta: meta,
372-
entryPoint: entryPoint,
373-
staticBase: staticBase,
374-
args: args,
375-
threads: threads,
376-
memory: mem,
377-
pageTable: pageTable,
378-
syms: syms,
379-
symErr: symErr,
380-
dwarf: dwarf,
381-
dwarfErr: dwarfErr,
382-
dwarfLoc: dwarfLoc,
383-
warnings: warnings,
396+
meta: meta,
397+
entryPoint: entryPoint,
398+
staticBase: staticBase,
399+
args: args,
400+
threads: threads,
401+
memory: mem,
402+
pageTable: pageTable,
403+
syms: syms,
404+
symErr: symErr,
405+
dwarf: dwarf,
406+
dwarfErr: dwarfErr,
407+
dwarfLoc: dwarfLoc,
408+
dwarfLocLists: dwarfLocLists,
409+
dwarfAddr: dwarfAddr,
410+
warnings: warnings,
384411
}
385412

386413
return p, nil

internal/gocore/dwarf.go

Lines changed: 93 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"golang.org/x/debug/dwtest"
1515
"golang.org/x/debug/internal/core"
1616

17+
"golang.org/x/debug/third_party/delve/dwarf/godwarf"
1718
"golang.org/x/debug/third_party/delve/dwarf/loclist"
1819
"golang.org/x/debug/third_party/delve/dwarf/op"
1920
"golang.org/x/debug/third_party/delve/dwarf/regnum"
@@ -386,15 +387,40 @@ func readDWARFVars(p *core.Process, fns *funcTab, dwarfTypeMap map[dwarf.Type]*T
386387
if err != nil {
387388
return nil, fmt.Errorf("failed to read DWARF: %v", err)
388389
}
390+
dLocListsSec, derr := p.DWARFLocLists()
391+
if derr != nil {
392+
return nil, fmt.Errorf("failed to read DWARF: %v", derr)
393+
}
394+
dAddrSec, daerr := p.DWARFAddr()
395+
if daerr != nil {
396+
return nil, fmt.Errorf("failed to read DWARF: %v", daerr)
397+
}
398+
debugAddrSec := godwarf.ParseAddr(dAddrSec)
389399
vars := make(map[*Func][]dwarfVar)
390400
var curfn *Func
391401
r := d.Reader()
402+
addrBase := uint64(0)
403+
unitVersion := 4
392404
for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() {
393405
if isNonGoCU(e) {
394406
r.SkipChildren()
395407
continue
396408
}
397-
409+
if e.Tag == dwarf.TagCompileUnit {
410+
// Determine whether we're looking at DWARF version 5 or
411+
// some version prior to 5. At the moment the DWARF
412+
// reading machinery in debug/dwarf keeps track of DWARF
413+
// version for each unit but doesn't expose this info to
414+
// clients, so we need to do the detection indirectly,
415+
// here by looking for an attribute that only gets generated
416+
// if DWARF 5 is being used.
417+
if f := e.AttrField(dwarf.AttrAddrBase); f != nil {
418+
addrBase = uint64(f.Val.(int64))
419+
unitVersion = 5
420+
} else {
421+
unitVersion = 4
422+
}
423+
}
398424
if e.Tag == dwarf.TagSubprogram {
399425
if e.AttrField(dwarf.AttrLowpc) == nil ||
400426
e.AttrField(dwarf.AttrHighpc) == nil {
@@ -459,30 +485,74 @@ func readDWARFVars(p *core.Process, fns *funcTab, dwarfTypeMap map[dwarf.Type]*T
459485
}
460486
name := nf.Val.(string)
461487

462-
// No .debug_loc section, can't make progress.
463-
if len(dLocSec) == 0 {
464-
continue
465-
}
488+
// FIXME A note on the code above: we're screening out
489+
// variables that don't have a specific name and type, which
490+
// on the surface seems workable, however this also rejects
491+
// vars corresponding to "concrete" instances of vars that
492+
// have been inlined, which is almost certainly a mistake. For
493+
// more on concrete/abstract variables see
494+
// https://github.com/golang/proposal/blob/master/design/22080-dwarf-inlining.md#how-the-generated-dwarf-should-look,
495+
// which has examples. Instead what the code above should be
496+
// doing is caching any abstract variables it encounters in a
497+
// map (indexed by DWARF offset), then when it encounters a
498+
// concrete var, pick the name and type up from the abstract
499+
// variable.
500+
501+
switch unitVersion {
502+
case 5:
503+
{
504+
// No .debug_loclists section, can't make progress.
505+
if len(dLocListsSec) == 0 {
506+
continue
507+
}
466508

467-
// Read the location list.
468-
locListOff := aloc.Val.(int64)
469-
dr := loclist.NewDwarf2Reader(dLocSec, int(p.PtrSize()))
470-
dr.Seek(int(locListOff))
471-
var base uint64
472-
var e loclist.Entry
473-
for dr.Next(&e) {
474-
if e.BaseAddressSelection() {
475-
base = e.HighPC + p.StaticBase()
476-
continue
509+
debugAddr := debugAddrSec.GetSubsection(addrBase)
510+
// Read the location lists.
511+
locListOff := aloc.Val.(int64)
512+
dr := loclist.NewDwarf5Reader(dLocListsSec)
513+
elist, err := dr.Enumerate(locListOff, p.StaticBase(), debugAddr)
514+
if err != nil {
515+
return nil, err
516+
}
517+
for _, e := range elist {
518+
vars[curfn] = append(vars[curfn], dwarfVar{
519+
lowPC: core.Address(e.LowPC),
520+
highPC: core.Address(e.HighPC),
521+
kind: kind,
522+
instr: e.Instr,
523+
name: name,
524+
typ: dwarfTypeMap[dt],
525+
})
526+
}
527+
}
528+
default:
529+
{
530+
// No .debug_loc section, can't make progress.
531+
if len(dLocSec) == 0 {
532+
continue
533+
}
534+
535+
// Read the location list.
536+
locListOff := aloc.Val.(int64)
537+
dr := loclist.NewDwarf2Reader(dLocSec, int(p.PtrSize()))
538+
dr.Seek(int(locListOff))
539+
var base uint64
540+
var e loclist.Entry
541+
for dr.Next(&e) {
542+
if e.BaseAddressSelection() {
543+
base = e.HighPC + p.StaticBase()
544+
continue
545+
}
546+
vars[curfn] = append(vars[curfn], dwarfVar{
547+
lowPC: core.Address(e.LowPC + base),
548+
highPC: core.Address(e.HighPC + base),
549+
kind: kind,
550+
instr: e.Instr,
551+
name: name,
552+
typ: dwarfTypeMap[dt],
553+
})
554+
}
477555
}
478-
vars[curfn] = append(vars[curfn], dwarfVar{
479-
lowPC: core.Address(e.LowPC + base),
480-
highPC: core.Address(e.HighPC + base),
481-
kind: kind,
482-
instr: e.Instr,
483-
name: name,
484-
typ: dwarfTypeMap[dt],
485-
})
486556
}
487557
}
488558
return vars, nil

0 commit comments

Comments
 (0)