Field: make([]Field, len(a.field)),
}
- // If we are currently parsing the compilation unit,
- // we can't evaluate Addrx or Strx until we've seen the
- // relevant base entry.
- type delayed struct {
- idx int
- off uint64
- fmt format
- }
- var delay []delayed
-
resolveStrx := func(strBase, off uint64) string {
off += strBase
if uint64(int(off)) != off {
return nil
}
- // We have to adjust by the offset of the
- // compilation unit. This won't work if the
- // program uses Reader.Seek to skip over the
- // unit. Not much we can do about that.
- var addrBase int64
- if cu != nil {
- addrBase, _ = cu.Val(AttrAddrBase).(int64)
- } else if a.tag == TagCompileUnit {
- delay = append(delay, delayed{i, off, formAddrx})
- break
- }
-
+ addrBase := int64(u.addrBase())
var err error
val, err = b.dwarf.debugAddr(b.format, uint64(addrBase), off)
if err != nil {
off *= 4
}
- // We have to adjust by the offset of the
- // compilation unit. This won't work if the
- // program uses Reader.Seek to skip over the
- // unit. Not much we can do about that.
- var strBase int64
- if cu != nil {
- strBase, _ = cu.Val(AttrStrOffsetsBase).(int64)
- } else if a.tag == TagCompileUnit {
- delay = append(delay, delayed{i, off, formStrx})
- break
- }
-
+ strBase := int64(u.strOffsetsBase())
val = resolveStrx(uint64(strBase), off)
case formStrpSup:
case formRnglistx:
off := b.uint()
- // We have to adjust by the rnglists_base of
- // the compilation unit. This won't work if
- // the program uses Reader.Seek to skip over
- // the unit. Not much we can do about that.
- var rnglistsBase int64
- if cu != nil {
- rnglistsBase, _ = cu.Val(AttrRnglistsBase).(int64)
- } else if a.tag == TagCompileUnit {
- delay = append(delay, delayed{i, off, formRnglistx})
- break
- }
-
+ rnglistsBase := int64(u.rngListsBase())
val = resolveRnglistx(uint64(rnglistsBase), off)
}
if b.err != nil {
return nil
}
-
- for _, del := range delay {
- switch del.fmt {
- case formAddrx:
- addrBase, _ := e.Val(AttrAddrBase).(int64)
- val, err := b.dwarf.debugAddr(b.format, uint64(addrBase), del.off)
- if err != nil {
- b.err = err
- return nil
- }
- e.Field[del.idx].Val = val
- case formStrx:
- strBase, _ := e.Val(AttrStrOffsetsBase).(int64)
- e.Field[del.idx].Val = resolveStrx(uint64(strBase), del.off)
- if b.err != nil {
- return nil
- }
- case formRnglistx:
- rnglistsBase, _ := e.Val(AttrRnglistsBase).(int64)
- e.Field[del.idx].Val = resolveRnglistx(uint64(rnglistsBase), del.off)
- if b.err != nil {
- return nil
- }
- }
- }
-
return e
}
u := &d.unit[0]
r.unit = 0
r.b = makeBuf(r.d, u, "info", u.off, u.data)
+ r.collectDwarf5BaseOffsets(u)
r.cu = nil
return
}
u := &d.unit[i]
r.unit = i
r.b = makeBuf(r.d, u, "info", off, u.data[off-u.off:])
+ r.collectDwarf5BaseOffsets(u)
}
// maybeNextUnit advances to the next unit if this one is finished.
u := &r.d.unit[r.unit]
r.b = makeBuf(r.d, u, "info", u.off, u.data)
r.cu = nil
+ r.collectDwarf5BaseOffsets(u)
+}
+
+func (r *Reader) collectDwarf5BaseOffsets(u *unit) {
+ if u.vers < 5 || u.unit5 != nil {
+ return
+ }
+ u.unit5 = new(unit5)
+ if err := r.d.collectDwarf5BaseOffsets(u); err != nil {
+ r.err = err
+ }
}
// Next reads the next entry from the encoded entry stream.
import (
. "debug/dwarf"
+ "debug/elf"
"encoding/binary"
"path/filepath"
"reflect"
t.Errorf("got non-nil entry0, wanted nil")
}
}
+
+func TestIssue57046(t *testing.T) {
+ f, err := elf.Open("testdata/issue57046-clang.elf5")
+ if err != nil {
+ t.Fatalf("elf.Open returns err: %v", err)
+ }
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("f.DWARF returns err: %v", err)
+ }
+ // Write down all the subprogram DIEs.
+ spdies := []Offset{}
+ lopcs := []uint64{}
+ r := d.Reader()
+ for {
+ e, err := r.Next()
+ if err != nil {
+ t.Fatalf("r.Next() returns err: %v", err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != TagSubprogram {
+ continue
+ }
+ var name string
+ var lopc uint64
+ if n, ok := e.Val(AttrName).(string); ok {
+ name = n
+ }
+ if lo, ok := e.Val(AttrLowpc).(uint64); ok {
+ lopc = lo
+ }
+ if name == "" || lopc == 0 {
+ continue
+ }
+ spdies = append(spdies, e.Offset)
+ lopcs = append(lopcs, lopc)
+ }
+
+ // Seek to the second entry in spdies (corresponding to mom() in
+ // issue57046_part2.c) and take a look at it.
+ r2 := d.Reader()
+ r2.Seek(spdies[1])
+ e, err := r2.Next()
+ if err != nil {
+ t.Fatalf("r2.Next() returns err: %v", err)
+ }
+ if e == nil {
+ t.Fatalf("r2.Next() returned nil")
+ }
+
+ // Verify that the lopc we see matches what we saw before.
+ got := e.Val(AttrLowpc).(uint64)
+ if got != lopcs[1] {
+ t.Errorf("bad lopc for fn2 following seek: want %x got %x\n",
+ lopcs[1], got)
+ }
+}
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Part 1 of the sources for issue 57046 test case.
+
+// Build instructions:
+//
+// clang-16 -O -g -gdwarf-5 -c issue57046_part1.c
+// clang-16 -O -g -gdwarf-5 -c issue57046_part2.c
+// clang-16 -o issue57046-clang.elf5 issue57046_part1.o issue57046_part2.o
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern const char *mom();
+
+int gadgety() {
+ const char *ev = getenv("PATH");
+ int n = strlen(ev);
+ int s1 = (int)ev[0];
+ int s2 = (int)ev[1];
+ int s3 = (int)ev[2];
+ for (int i = 0; i < strlen(ev); i++) {
+ if (s1 == 101) {
+ int t = s1;
+ s1 = s3;
+ s3 = t;
+ }
+ if (ev[i] == 99) {
+ printf("%d\n", i);
+ }
+ }
+ s2 *= 2;
+ return n + s1 + s2;
+}
+
+int main(int argc, char **argv) {
+ printf("Hi %s %d\n", mom(), gadgety());
+ return 0;
+}
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Part 2 of the sources for issue 57046 test case.
+// See part 1 for build instructions.
+
+const char *mom() {
+ return "mom";
+}
off Offset // byte offset of data within the aggregate info
data []byte
atable abbrevTable
+ *unit5 // info specific to DWARF 5 units
asize int
vers int
- utype uint8 // DWARF 5 unit type
is64 bool // True for 64-bit DWARF format
+ utype uint8 // DWARF 5 unit type
+}
+
+type unit5 struct {
+ addrBase uint64
+ strOffsetsBase uint64
+ rngListsBase uint64
+ locListsBase uint64
}
// Implement the dataFormat interface.
return u.asize
}
+func (u *unit) addrBase() uint64 {
+ if u.unit5 != nil {
+ return u.unit5.addrBase
+ }
+ return 0
+}
+
+func (u *unit) strOffsetsBase() uint64 {
+ if u.unit5 != nil {
+ return u.unit5.strOffsetsBase
+ }
+ return 0
+}
+
+func (u *unit) rngListsBase() uint64 {
+ if u.unit5 != nil {
+ return u.unit5.rngListsBase
+ }
+ return 0
+}
+
+func (u *unit) locListsBase() uint64 {
+ if u.unit5 != nil {
+ return u.unit5.locListsBase
+ }
+ return 0
+}
+
func (d *Data) parseUnits() ([]unit, error) {
// Count units.
nunit := 0
}
return -1
}
+
+func (d *Data) collectDwarf5BaseOffsets(u *unit) error {
+ if u.unit5 == nil {
+ panic("expected unit5 to be set up already")
+ }
+ b := makeBuf(d, u, "info", u.off, u.data)
+ cu := b.entry(nil, u)
+ if cu == nil {
+ // Unknown abbreviation table entry or some other fatal
+ // problem; bail early on the assumption that this will be
+ // detected at some later point.
+ return b.err
+ }
+ if iAddrBase, ok := cu.Val(AttrAddrBase).(int64); ok {
+ u.unit5.addrBase = uint64(iAddrBase)
+ }
+ if iStrOffsetsBase, ok := cu.Val(AttrStrOffsetsBase).(int64); ok {
+ u.unit5.strOffsetsBase = uint64(iStrOffsetsBase)
+ }
+ if iRngListsBase, ok := cu.Val(AttrRnglistsBase).(int64); ok {
+ u.unit5.rngListsBase = uint64(iRngListsBase)
+ }
+ if iLocListsBase, ok := cu.Val(AttrLoclistsBase).(int64); ok {
+ u.unit5.locListsBase = uint64(iLocListsBase)
+ }
+ return nil
+}