Skip to content

Commit e256e64

Browse files
committed
debug/dwarf: fix problem with DWARF 5 and Seek method
When clients use debug/dwarf to examine DWARF 5 binaries, we can run into problems when the Seek() method is used to skip ahead from a DIE in one compilation unit to a DIE in another unit. The problem here is that it is common for DWARF 5 comp units to have attributes (ex: DW_AT_addr_base) whose value must be applied as an offset when reading certain forms (ex: DW_FORM_addrx) within that unit. The existing implementation didn't have a good way to recover these attrs following the Seek call, and had to essentially punt in this case, resulting in incorrect attr values. This patch adds new support for reading and caching the key comp unit DIE attributes (DW_AT_addr_base, DW_AT_loclists_base, etc) prior to visiting any of the DIE entries in a unit, storing the cache values of these attrs the main table of units. This base attribute reading/caching behavior also happens (where needed) after Seek calls. Should resolve delve issue 3861. Supercedes Go pull request 70400. Updates #26379. Fixes #57046. Change-Id: I536a57e2ba4fc55132d91c7f36f67a91ac408dc3 Reviewed-on: https://go-review.googlesource.com/c/go/+/655976 Reviewed-by: Alessandro Arzilli <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent c3e7d5f commit e256e64

File tree

6 files changed

+192
-73
lines changed

6 files changed

+192
-73
lines changed

src/debug/dwarf/entry.go

+16-72
Original file line numberDiff line numberDiff line change
@@ -426,16 +426,6 @@ func (b *buf) entry(cu *Entry, u *unit) *Entry {
426426
Field: make([]Field, len(a.field)),
427427
}
428428

429-
// If we are currently parsing the compilation unit,
430-
// we can't evaluate Addrx or Strx until we've seen the
431-
// relevant base entry.
432-
type delayed struct {
433-
idx int
434-
off uint64
435-
fmt format
436-
}
437-
var delay []delayed
438-
439429
resolveStrx := func(strBase, off uint64) string {
440430
off += strBase
441431
if uint64(int(off)) != off {
@@ -532,18 +522,7 @@ func (b *buf) entry(cu *Entry, u *unit) *Entry {
532522
return nil
533523
}
534524

535-
// We have to adjust by the offset of the
536-
// compilation unit. This won't work if the
537-
// program uses Reader.Seek to skip over the
538-
// unit. Not much we can do about that.
539-
var addrBase int64
540-
if cu != nil {
541-
addrBase, _ = cu.Val(AttrAddrBase).(int64)
542-
} else if a.tag == TagCompileUnit {
543-
delay = append(delay, delayed{i, off, formAddrx})
544-
break
545-
}
546-
525+
addrBase := int64(u.addrBase())
547526
var err error
548527
val, err = b.dwarf.debugAddr(b.format, uint64(addrBase), off)
549528
if err != nil {
@@ -683,18 +662,7 @@ func (b *buf) entry(cu *Entry, u *unit) *Entry {
683662
off *= 4
684663
}
685664

686-
// We have to adjust by the offset of the
687-
// compilation unit. This won't work if the
688-
// program uses Reader.Seek to skip over the
689-
// unit. Not much we can do about that.
690-
var strBase int64
691-
if cu != nil {
692-
strBase, _ = cu.Val(AttrStrOffsetsBase).(int64)
693-
} else if a.tag == TagCompileUnit {
694-
delay = append(delay, delayed{i, off, formStrx})
695-
break
696-
}
697-
665+
strBase := int64(u.strOffsetsBase())
698666
val = resolveStrx(uint64(strBase), off)
699667

700668
case formStrpSup:
@@ -743,18 +711,7 @@ func (b *buf) entry(cu *Entry, u *unit) *Entry {
743711
case formRnglistx:
744712
off := b.uint()
745713

746-
// We have to adjust by the rnglists_base of
747-
// the compilation unit. This won't work if
748-
// the program uses Reader.Seek to skip over
749-
// the unit. Not much we can do about that.
750-
var rnglistsBase int64
751-
if cu != nil {
752-
rnglistsBase, _ = cu.Val(AttrRnglistsBase).(int64)
753-
} else if a.tag == TagCompileUnit {
754-
delay = append(delay, delayed{i, off, formRnglistx})
755-
break
756-
}
757-
714+
rnglistsBase := int64(u.rngListsBase())
758715
val = resolveRnglistx(uint64(rnglistsBase), off)
759716
}
760717

@@ -763,32 +720,6 @@ func (b *buf) entry(cu *Entry, u *unit) *Entry {
763720
if b.err != nil {
764721
return nil
765722
}
766-
767-
for _, del := range delay {
768-
switch del.fmt {
769-
case formAddrx:
770-
addrBase, _ := e.Val(AttrAddrBase).(int64)
771-
val, err := b.dwarf.debugAddr(b.format, uint64(addrBase), del.off)
772-
if err != nil {
773-
b.err = err
774-
return nil
775-
}
776-
e.Field[del.idx].Val = val
777-
case formStrx:
778-
strBase, _ := e.Val(AttrStrOffsetsBase).(int64)
779-
e.Field[del.idx].Val = resolveStrx(uint64(strBase), del.off)
780-
if b.err != nil {
781-
return nil
782-
}
783-
case formRnglistx:
784-
rnglistsBase, _ := e.Val(AttrRnglistsBase).(int64)
785-
e.Field[del.idx].Val = resolveRnglistx(uint64(rnglistsBase), del.off)
786-
if b.err != nil {
787-
return nil
788-
}
789-
}
790-
}
791-
792723
return e
793724
}
794725

@@ -840,6 +771,7 @@ func (r *Reader) Seek(off Offset) {
840771
u := &d.unit[0]
841772
r.unit = 0
842773
r.b = makeBuf(r.d, u, "info", u.off, u.data)
774+
r.collectDwarf5BaseOffsets(u)
843775
r.cu = nil
844776
return
845777
}
@@ -855,6 +787,7 @@ func (r *Reader) Seek(off Offset) {
855787
u := &d.unit[i]
856788
r.unit = i
857789
r.b = makeBuf(r.d, u, "info", off, u.data[off-u.off:])
790+
r.collectDwarf5BaseOffsets(u)
858791
}
859792

860793
// maybeNextUnit advances to the next unit if this one is finished.
@@ -870,6 +803,17 @@ func (r *Reader) nextUnit() {
870803
u := &r.d.unit[r.unit]
871804
r.b = makeBuf(r.d, u, "info", u.off, u.data)
872805
r.cu = nil
806+
r.collectDwarf5BaseOffsets(u)
807+
}
808+
809+
func (r *Reader) collectDwarf5BaseOffsets(u *unit) {
810+
if u.vers < 5 || u.unit5 != nil {
811+
return
812+
}
813+
u.unit5 = new(unit5)
814+
if err := r.d.collectDwarf5BaseOffsets(u); err != nil {
815+
r.err = err
816+
}
873817
}
874818

875819
// Next reads the next entry from the encoded entry stream.

src/debug/dwarf/entry_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package dwarf_test
66

77
import (
88
. "debug/dwarf"
9+
"debug/elf"
910
"encoding/binary"
1011
"path/filepath"
1112
"reflect"
@@ -457,3 +458,62 @@ func TestIssue52045(t *testing.T) {
457458
t.Errorf("got non-nil entry0, wanted nil")
458459
}
459460
}
461+
462+
func TestIssue57046(t *testing.T) {
463+
f, err := elf.Open("testdata/issue57046-clang.elf5")
464+
if err != nil {
465+
t.Fatalf("elf.Open returns err: %v", err)
466+
}
467+
d, err := f.DWARF()
468+
if err != nil {
469+
t.Fatalf("f.DWARF returns err: %v", err)
470+
}
471+
// Write down all the subprogram DIEs.
472+
spdies := []Offset{}
473+
lopcs := []uint64{}
474+
r := d.Reader()
475+
for {
476+
e, err := r.Next()
477+
if err != nil {
478+
t.Fatalf("r.Next() returns err: %v", err)
479+
}
480+
if e == nil {
481+
break
482+
}
483+
if e.Tag != TagSubprogram {
484+
continue
485+
}
486+
var name string
487+
var lopc uint64
488+
if n, ok := e.Val(AttrName).(string); ok {
489+
name = n
490+
}
491+
if lo, ok := e.Val(AttrLowpc).(uint64); ok {
492+
lopc = lo
493+
}
494+
if name == "" || lopc == 0 {
495+
continue
496+
}
497+
spdies = append(spdies, e.Offset)
498+
lopcs = append(lopcs, lopc)
499+
}
500+
501+
// Seek to the second entry in spdies (corresponding to mom() in
502+
// issue57046_part2.c) and take a look at it.
503+
r2 := d.Reader()
504+
r2.Seek(spdies[1])
505+
e, err := r2.Next()
506+
if err != nil {
507+
t.Fatalf("r2.Next() returns err: %v", err)
508+
}
509+
if e == nil {
510+
t.Fatalf("r2.Next() returned nil")
511+
}
512+
513+
// Verify that the lopc we see matches what we saw before.
514+
got := e.Val(AttrLowpc).(uint64)
515+
if got != lopcs[1] {
516+
t.Errorf("bad lopc for fn2 following seek: want %x got %x\n",
517+
lopcs[1], got)
518+
}
519+
}
18.9 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Part 1 of the sources for issue 57046 test case.
6+
7+
// Build instructions:
8+
//
9+
// clang-16 -O -g -gdwarf-5 -c issue57046_part1.c
10+
// clang-16 -O -g -gdwarf-5 -c issue57046_part2.c
11+
// clang-16 -o issue57046-clang.elf5 issue57046_part1.o issue57046_part2.o
12+
13+
#include <stdio.h>
14+
#include <stdlib.h>
15+
#include <string.h>
16+
17+
extern const char *mom();
18+
19+
int gadgety() {
20+
const char *ev = getenv("PATH");
21+
int n = strlen(ev);
22+
int s1 = (int)ev[0];
23+
int s2 = (int)ev[1];
24+
int s3 = (int)ev[2];
25+
for (int i = 0; i < strlen(ev); i++) {
26+
if (s1 == 101) {
27+
int t = s1;
28+
s1 = s3;
29+
s3 = t;
30+
}
31+
if (ev[i] == 99) {
32+
printf("%d\n", i);
33+
}
34+
}
35+
s2 *= 2;
36+
return n + s1 + s2;
37+
}
38+
39+
int main(int argc, char **argv) {
40+
printf("Hi %s %d\n", mom(), gadgety());
41+
return 0;
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Part 2 of the sources for issue 57046 test case.
6+
// See part 1 for build instructions.
7+
8+
const char *mom() {
9+
return "mom";
10+
}

src/debug/dwarf/unit.go

+64-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,18 @@ type unit struct {
1717
off Offset // byte offset of data within the aggregate info
1818
data []byte
1919
atable abbrevTable
20+
*unit5 // info specific to DWARF 5 units
2021
asize int
2122
vers int
22-
utype uint8 // DWARF 5 unit type
2323
is64 bool // True for 64-bit DWARF format
24+
utype uint8 // DWARF 5 unit type
25+
}
26+
27+
type unit5 struct {
28+
addrBase uint64
29+
strOffsetsBase uint64
30+
rngListsBase uint64
31+
locListsBase uint64
2432
}
2533

2634
// Implement the dataFormat interface.
@@ -37,6 +45,34 @@ func (u *unit) addrsize() int {
3745
return u.asize
3846
}
3947

48+
func (u *unit) addrBase() uint64 {
49+
if u.unit5 != nil {
50+
return u.unit5.addrBase
51+
}
52+
return 0
53+
}
54+
55+
func (u *unit) strOffsetsBase() uint64 {
56+
if u.unit5 != nil {
57+
return u.unit5.strOffsetsBase
58+
}
59+
return 0
60+
}
61+
62+
func (u *unit) rngListsBase() uint64 {
63+
if u.unit5 != nil {
64+
return u.unit5.rngListsBase
65+
}
66+
return 0
67+
}
68+
69+
func (u *unit) locListsBase() uint64 {
70+
if u.unit5 != nil {
71+
return u.unit5.locListsBase
72+
}
73+
return 0
74+
}
75+
4076
func (d *Data) parseUnits() ([]unit, error) {
4177
// Count units.
4278
nunit := 0
@@ -135,3 +171,30 @@ func (d *Data) offsetToUnit(off Offset) int {
135171
}
136172
return -1
137173
}
174+
175+
func (d *Data) collectDwarf5BaseOffsets(u *unit) error {
176+
if u.unit5 == nil {
177+
panic("expected unit5 to be set up already")
178+
}
179+
b := makeBuf(d, u, "info", u.off, u.data)
180+
cu := b.entry(nil, u)
181+
if cu == nil {
182+
// Unknown abbreviation table entry or some other fatal
183+
// problem; bail early on the assumption that this will be
184+
// detected at some later point.
185+
return b.err
186+
}
187+
if iAddrBase, ok := cu.Val(AttrAddrBase).(int64); ok {
188+
u.unit5.addrBase = uint64(iAddrBase)
189+
}
190+
if iStrOffsetsBase, ok := cu.Val(AttrStrOffsetsBase).(int64); ok {
191+
u.unit5.strOffsetsBase = uint64(iStrOffsetsBase)
192+
}
193+
if iRngListsBase, ok := cu.Val(AttrRnglistsBase).(int64); ok {
194+
u.unit5.rngListsBase = uint64(iRngListsBase)
195+
}
196+
if iLocListsBase, ok := cu.Val(AttrLoclistsBase).(int64); ok {
197+
u.unit5.locListsBase = uint64(iLocListsBase)
198+
}
199+
return nil
200+
}

0 commit comments

Comments
 (0)