Skip to content

Commit 8850c75

Browse files
thanmmknyszek
authored andcommitted
third_party/delve/dwarf: refresh to add support for DWARF 5
This patch pulls in some additional pieces from Delve that add support for DWARF 5. Prior to this point the code copied from delve here was specific to DWARF 2; once the Go compiler starts generating DWARF version 5 we need more machinery to handle that. Updates golang/go#26379. Change-Id: Ib7bee1101101459e8eb5f7c62c625f0b7b53411f Reviewed-on: https://go-review.googlesource.com/c/debug/+/654255 Reviewed-by: Michael Pratt <[email protected]> Reviewed-by: Junyang Shao <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 1e0cbda commit 8850c75

File tree

5 files changed

+261
-2
lines changed

5 files changed

+261
-2
lines changed

third_party/delve/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ This is a partial import of github.com/go-delve/delve/pkg focusing
44
primarily on DWARF utilities. The most important part copied over is
55
the stack program interpreter, though the regnum package is useful too.
66

7-
Copied at commit 84c99e508c835a9b9d44bbde9558496d3583256c.
7+
Copied at commit e6e7aeb667057ad6a98120ebf3591fbafdedb19d, with additions to support DWARF5.
8+
9+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package godwarf
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"errors"
7+
8+
"golang.org/x/debug/third_party/delve/dwarf"
9+
)
10+
11+
// DebugAddrSection represents the debug_addr section of DWARFv5.
12+
// See DWARFv5 section 7.27 page 241 and following.
13+
type DebugAddrSection struct {
14+
byteOrder binary.ByteOrder
15+
ptrSz int
16+
data []byte
17+
}
18+
19+
// ParseAddr parses the header of a debug_addr section.
20+
func ParseAddr(data []byte) *DebugAddrSection {
21+
if len(data) == 0 {
22+
return nil
23+
}
24+
r := &DebugAddrSection{data: data}
25+
_, dwarf64, _, byteOrder := dwarf.ReadDwarfLengthVersion(data)
26+
r.byteOrder = byteOrder
27+
data = data[6:]
28+
if dwarf64 {
29+
data = data[8:]
30+
}
31+
32+
addrSz := data[0]
33+
segSelSz := data[1]
34+
r.ptrSz = int(addrSz + segSelSz)
35+
36+
return r
37+
}
38+
39+
// GetSubsection returns the subsection of debug_addr starting at addrBase
40+
func (addr *DebugAddrSection) GetSubsection(addrBase uint64) *DebugAddr {
41+
if addr == nil {
42+
return nil
43+
}
44+
return &DebugAddr{DebugAddrSection: addr, addrBase: addrBase}
45+
}
46+
47+
// DebugAddr represents a subsection of the debug_addr section with a specific base address
48+
type DebugAddr struct {
49+
*DebugAddrSection
50+
addrBase uint64
51+
}
52+
53+
// Get returns the address at index idx starting from addrBase.
54+
func (addr *DebugAddr) Get(idx uint64) (uint64, error) {
55+
if addr == nil || addr.DebugAddrSection == nil {
56+
return 0, errors.New("debug_addr section not present")
57+
}
58+
off := idx*uint64(addr.ptrSz) + addr.addrBase
59+
return dwarf.ReadUintRaw(bytes.NewReader(addr.data[off:]), addr.byteOrder, addr.ptrSz)
60+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package loclist
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"fmt"
7+
8+
"golang.org/x/debug/third_party/delve/dwarf"
9+
"golang.org/x/debug/third_party/delve/dwarf/godwarf"
10+
"golang.org/x/debug/third_party/delve/dwarf/leb128"
11+
)
12+
13+
// Dwarf5Reader parses and presents DWARF loclist information for DWARF version 5 and later.
14+
// See DWARFv5 section 7.29 page 243 and following.
15+
type Dwarf5Reader struct {
16+
byteOrder binary.ByteOrder
17+
ptrSz int
18+
data []byte
19+
}
20+
21+
func NewDwarf5Reader(data []byte) *Dwarf5Reader {
22+
if len(data) == 0 {
23+
return nil
24+
}
25+
r := &Dwarf5Reader{data: data}
26+
27+
_, dwarf64, _, byteOrder := dwarf.ReadDwarfLengthVersion(data)
28+
r.byteOrder = byteOrder
29+
30+
data = data[6:]
31+
if dwarf64 {
32+
data = data[8:]
33+
}
34+
35+
addrSz := data[0]
36+
segSelSz := data[1]
37+
r.ptrSz = int(addrSz + segSelSz)
38+
39+
// Not read:
40+
// - offset_entry_count (4 bytes)
41+
// - offset table (offset_entry_count*4 or offset_entry_count*8 if dwarf64 is set)
42+
43+
return r
44+
}
45+
46+
func (rdr *Dwarf5Reader) Empty() bool {
47+
return rdr == nil
48+
}
49+
50+
type loclistsIterator struct {
51+
rdr *Dwarf5Reader
52+
debugAddr *godwarf.DebugAddr
53+
buf *bytes.Buffer
54+
staticBase uint64
55+
base uint64 // base for offsets in the list
56+
57+
onRange bool
58+
atEnd bool
59+
start, end uint64
60+
instr []byte
61+
defaultInstr []byte
62+
err error
63+
}
64+
65+
const (
66+
_DW_LLE_end_of_list uint8 = 0x0
67+
_DW_LLE_base_addressx uint8 = 0x1
68+
_DW_LLE_startx_endx uint8 = 0x2
69+
_DW_LLE_startx_length uint8 = 0x3
70+
_DW_LLE_offset_pair uint8 = 0x4
71+
_DW_LLE_default_location uint8 = 0x5
72+
_DW_LLE_base_address uint8 = 0x6
73+
_DW_LLE_start_end uint8 = 0x7
74+
_DW_LLE_start_length uint8 = 0x8
75+
)
76+
77+
func (it *loclistsIterator) next() bool {
78+
if it.err != nil || it.atEnd {
79+
return false
80+
}
81+
opcode, err := it.buf.ReadByte()
82+
if err != nil {
83+
it.err = err
84+
return false
85+
}
86+
switch opcode {
87+
case _DW_LLE_end_of_list:
88+
it.atEnd = true
89+
it.onRange = false
90+
return false
91+
92+
case _DW_LLE_base_addressx:
93+
baseIdx, _ := leb128.DecodeUnsigned(it.buf)
94+
it.base, it.err = it.debugAddr.Get(baseIdx)
95+
it.base += it.staticBase
96+
it.onRange = false
97+
98+
case _DW_LLE_startx_endx:
99+
startIdx, _ := leb128.DecodeUnsigned(it.buf)
100+
endIdx, _ := leb128.DecodeUnsigned(it.buf)
101+
it.readInstr()
102+
103+
it.start, it.err = it.debugAddr.Get(startIdx)
104+
if it.err == nil {
105+
it.end, it.err = it.debugAddr.Get(endIdx)
106+
}
107+
it.onRange = true
108+
109+
case _DW_LLE_startx_length:
110+
startIdx, _ := leb128.DecodeUnsigned(it.buf)
111+
length, _ := leb128.DecodeUnsigned(it.buf)
112+
it.readInstr()
113+
114+
it.start, it.err = it.debugAddr.Get(startIdx)
115+
it.end = it.start + length
116+
it.onRange = true
117+
118+
case _DW_LLE_offset_pair:
119+
off1, _ := leb128.DecodeUnsigned(it.buf)
120+
off2, _ := leb128.DecodeUnsigned(it.buf)
121+
it.readInstr()
122+
123+
it.start = it.base + off1
124+
it.end = it.base + off2
125+
it.onRange = true
126+
127+
case _DW_LLE_default_location:
128+
it.readInstr()
129+
it.defaultInstr = it.instr
130+
it.onRange = false
131+
132+
case _DW_LLE_base_address:
133+
it.base, it.err = dwarf.ReadUintRaw(it.buf, it.rdr.byteOrder, it.rdr.ptrSz)
134+
it.base += it.staticBase
135+
it.onRange = false
136+
137+
case _DW_LLE_start_end:
138+
it.start, it.err = dwarf.ReadUintRaw(it.buf, it.rdr.byteOrder, it.rdr.ptrSz)
139+
it.end, it.err = dwarf.ReadUintRaw(it.buf, it.rdr.byteOrder, it.rdr.ptrSz)
140+
it.readInstr()
141+
it.onRange = true
142+
143+
case _DW_LLE_start_length:
144+
it.start, it.err = dwarf.ReadUintRaw(it.buf, it.rdr.byteOrder, it.rdr.ptrSz)
145+
length, _ := leb128.DecodeUnsigned(it.buf)
146+
it.readInstr()
147+
it.end = it.start + length
148+
it.onRange = true
149+
150+
default:
151+
it.err = fmt.Errorf("unknown opcode %#x at %#x", opcode, len(it.rdr.data)-it.buf.Len())
152+
it.onRange = false
153+
it.atEnd = true
154+
return false
155+
}
156+
157+
return true
158+
}
159+
160+
func (it *loclistsIterator) readInstr() {
161+
length, _ := leb128.DecodeUnsigned(it.buf)
162+
it.instr = it.buf.Next(int(length))
163+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package loclist
2+
3+
import (
4+
"bytes"
5+
6+
"golang.org/x/debug/third_party/delve/dwarf/godwarf"
7+
)
8+
9+
// Enumerate walks through all of the location list entries for a
10+
// given variable in a given function and enumerates them, returning
11+
// to the client. Note that this function doesn't exist in the delve
12+
// source; it was written here so as to be able to do something
13+
// similar to what's done with DWARF 2 location lists. Here off is the
14+
// offset within the .debug_loclists section containing the start of
15+
// the entries for the function in question, staticBase is the
16+
// start-of-text address for the executable, and debugAddr
17+
// encapsulates the portion of the .debug_addr section containing
18+
// entries for the current compilation unit.
19+
func (rdr *Dwarf5Reader) Enumerate(off int64, staticBase uint64, debugAddr *godwarf.DebugAddr) ([]Entry, error) {
20+
result := []Entry{}
21+
22+
it := &loclistsIterator{rdr: rdr, debugAddr: debugAddr, buf: bytes.NewBuffer(rdr.data), staticBase: staticBase}
23+
it.buf.Next(int(off))
24+
25+
for it.next() {
26+
if !it.onRange {
27+
continue
28+
}
29+
e := Entry{it.start, it.end, it.instr}
30+
result = append(result, e)
31+
}
32+
33+
return result, it.err
34+
}

third_party/delve/dwarf/parseutil.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func ReadUnitVersions(data []byte) map[dwarf.Offset]uint8 {
131131

132132
switch unitType {
133133
case _DW_UT_compile, _DW_UT_partial:
134-
headerSize = 5 + secoffsz
134+
headerSize = 4 + secoffsz
135135

136136
case _DW_UT_skeleton, _DW_UT_split_compile:
137137
headerSize = 4 + secoffsz + 8

0 commit comments

Comments
 (0)