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 <mpratt@google.com> Reviewed-by: Junyang Shao <shaojunyang@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/third_party/delve/README.md b/third_party/delve/README.md index 89ef824..67d3252 100644 --- a/third_party/delve/README.md +++ b/third_party/delve/README.md
@@ -4,4 +4,6 @@ primarily on DWARF utilities. The most important part copied over is the stack program interpreter, though the regnum package is useful too. -Copied at commit 84c99e508c835a9b9d44bbde9558496d3583256c. +Copied at commit e6e7aeb667057ad6a98120ebf3591fbafdedb19d, with additions to support DWARF5. + +
diff --git a/third_party/delve/dwarf/godwarf/addr.go b/third_party/delve/dwarf/godwarf/addr.go new file mode 100644 index 0000000..6c5bf48 --- /dev/null +++ b/third_party/delve/dwarf/godwarf/addr.go
@@ -0,0 +1,60 @@ +package godwarf + +import ( + "bytes" + "encoding/binary" + "errors" + + "golang.org/x/debug/third_party/delve/dwarf" +) + +// DebugAddrSection represents the debug_addr section of DWARFv5. +// See DWARFv5 section 7.27 page 241 and following. +type DebugAddrSection struct { + byteOrder binary.ByteOrder + ptrSz int + data []byte +} + +// ParseAddr parses the header of a debug_addr section. +func ParseAddr(data []byte) *DebugAddrSection { + if len(data) == 0 { + return nil + } + r := &DebugAddrSection{data: data} + _, dwarf64, _, byteOrder := dwarf.ReadDwarfLengthVersion(data) + r.byteOrder = byteOrder + data = data[6:] + if dwarf64 { + data = data[8:] + } + + addrSz := data[0] + segSelSz := data[1] + r.ptrSz = int(addrSz + segSelSz) + + return r +} + +// GetSubsection returns the subsection of debug_addr starting at addrBase +func (addr *DebugAddrSection) GetSubsection(addrBase uint64) *DebugAddr { + if addr == nil { + return nil + } + return &DebugAddr{DebugAddrSection: addr, addrBase: addrBase} +} + +// DebugAddr represents a subsection of the debug_addr section with a specific base address +type DebugAddr struct { + *DebugAddrSection + addrBase uint64 +} + +// Get returns the address at index idx starting from addrBase. +func (addr *DebugAddr) Get(idx uint64) (uint64, error) { + if addr == nil || addr.DebugAddrSection == nil { + return 0, errors.New("debug_addr section not present") + } + off := idx*uint64(addr.ptrSz) + addr.addrBase + return dwarf.ReadUintRaw(bytes.NewReader(addr.data[off:]), addr.byteOrder, addr.ptrSz) +}
diff --git a/third_party/delve/dwarf/loclist/dwarf5_loclist.go b/third_party/delve/dwarf/loclist/dwarf5_loclist.go new file mode 100644 index 0000000..d5f98fd --- /dev/null +++ b/third_party/delve/dwarf/loclist/dwarf5_loclist.go
@@ -0,0 +1,163 @@ +package loclist + +import ( + "bytes" + "encoding/binary" + "fmt" + + "golang.org/x/debug/third_party/delve/dwarf" + "golang.org/x/debug/third_party/delve/dwarf/godwarf" + "golang.org/x/debug/third_party/delve/dwarf/leb128" +) + +// Dwarf5Reader parses and presents DWARF loclist information for DWARF version 5 and later. +// See DWARFv5 section 7.29 page 243 and following. +type Dwarf5Reader struct { + byteOrder binary.ByteOrder + ptrSz int + data []byte +} + +func NewDwarf5Reader(data []byte) *Dwarf5Reader { + if len(data) == 0 { + return nil + } + r := &Dwarf5Reader{data: data} + + _, dwarf64, _, byteOrder := dwarf.ReadDwarfLengthVersion(data) + r.byteOrder = byteOrder + + data = data[6:] + if dwarf64 { + data = data[8:] + } + + addrSz := data[0] + segSelSz := data[1] + r.ptrSz = int(addrSz + segSelSz) + + // Not read: + // - offset_entry_count (4 bytes) + // - offset table (offset_entry_count*4 or offset_entry_count*8 if dwarf64 is set) + + return r +} + +func (rdr *Dwarf5Reader) Empty() bool { + return rdr == nil +} + +type loclistsIterator struct { + rdr *Dwarf5Reader + debugAddr *godwarf.DebugAddr + buf *bytes.Buffer + staticBase uint64 + base uint64 // base for offsets in the list + + onRange bool + atEnd bool + start, end uint64 + instr []byte + defaultInstr []byte + err error +} + +const ( + _DW_LLE_end_of_list uint8 = 0x0 + _DW_LLE_base_addressx uint8 = 0x1 + _DW_LLE_startx_endx uint8 = 0x2 + _DW_LLE_startx_length uint8 = 0x3 + _DW_LLE_offset_pair uint8 = 0x4 + _DW_LLE_default_location uint8 = 0x5 + _DW_LLE_base_address uint8 = 0x6 + _DW_LLE_start_end uint8 = 0x7 + _DW_LLE_start_length uint8 = 0x8 +) + +func (it *loclistsIterator) next() bool { + if it.err != nil || it.atEnd { + return false + } + opcode, err := it.buf.ReadByte() + if err != nil { + it.err = err + return false + } + switch opcode { + case _DW_LLE_end_of_list: + it.atEnd = true + it.onRange = false + return false + + case _DW_LLE_base_addressx: + baseIdx, _ := leb128.DecodeUnsigned(it.buf) + it.base, it.err = it.debugAddr.Get(baseIdx) + it.base += it.staticBase + it.onRange = false + + case _DW_LLE_startx_endx: + startIdx, _ := leb128.DecodeUnsigned(it.buf) + endIdx, _ := leb128.DecodeUnsigned(it.buf) + it.readInstr() + + it.start, it.err = it.debugAddr.Get(startIdx) + if it.err == nil { + it.end, it.err = it.debugAddr.Get(endIdx) + } + it.onRange = true + + case _DW_LLE_startx_length: + startIdx, _ := leb128.DecodeUnsigned(it.buf) + length, _ := leb128.DecodeUnsigned(it.buf) + it.readInstr() + + it.start, it.err = it.debugAddr.Get(startIdx) + it.end = it.start + length + it.onRange = true + + case _DW_LLE_offset_pair: + off1, _ := leb128.DecodeUnsigned(it.buf) + off2, _ := leb128.DecodeUnsigned(it.buf) + it.readInstr() + + it.start = it.base + off1 + it.end = it.base + off2 + it.onRange = true + + case _DW_LLE_default_location: + it.readInstr() + it.defaultInstr = it.instr + it.onRange = false + + case _DW_LLE_base_address: + it.base, it.err = dwarf.ReadUintRaw(it.buf, it.rdr.byteOrder, it.rdr.ptrSz) + it.base += it.staticBase + it.onRange = false + + case _DW_LLE_start_end: + it.start, it.err = dwarf.ReadUintRaw(it.buf, it.rdr.byteOrder, it.rdr.ptrSz) + it.end, it.err = dwarf.ReadUintRaw(it.buf, it.rdr.byteOrder, it.rdr.ptrSz) + it.readInstr() + it.onRange = true + + case _DW_LLE_start_length: + it.start, it.err = dwarf.ReadUintRaw(it.buf, it.rdr.byteOrder, it.rdr.ptrSz) + length, _ := leb128.DecodeUnsigned(it.buf) + it.readInstr() + it.end = it.start + length + it.onRange = true + + default: + it.err = fmt.Errorf("unknown opcode %#x at %#x", opcode, len(it.rdr.data)-it.buf.Len()) + it.onRange = false + it.atEnd = true + return false + } + + return true +} + +func (it *loclistsIterator) readInstr() { + length, _ := leb128.DecodeUnsigned(it.buf) + it.instr = it.buf.Next(int(length)) +}
diff --git a/third_party/delve/dwarf/loclist/dwarf5_loclist_additions.go b/third_party/delve/dwarf/loclist/dwarf5_loclist_additions.go new file mode 100644 index 0000000..37f75b8 --- /dev/null +++ b/third_party/delve/dwarf/loclist/dwarf5_loclist_additions.go
@@ -0,0 +1,34 @@ +package loclist + +import ( + "bytes" + + "golang.org/x/debug/third_party/delve/dwarf/godwarf" +) + +// Enumerate walks through all of the location list entries for a +// given variable in a given function and enumerates them, returning +// to the client. Note that this function doesn't exist in the delve +// source; it was written here so as to be able to do something +// similar to what's done with DWARF 2 location lists. Here off is the +// offset within the .debug_loclists section containing the start of +// the entries for the function in question, staticBase is the +// start-of-text address for the executable, and debugAddr +// encapsulates the portion of the .debug_addr section containing +// entries for the current compilation unit. +func (rdr *Dwarf5Reader) Enumerate(off int64, staticBase uint64, debugAddr *godwarf.DebugAddr) ([]Entry, error) { + result := []Entry{} + + it := &loclistsIterator{rdr: rdr, debugAddr: debugAddr, buf: bytes.NewBuffer(rdr.data), staticBase: staticBase} + it.buf.Next(int(off)) + + for it.next() { + if !it.onRange { + continue + } + e := Entry{it.start, it.end, it.instr} + result = append(result, e) + } + + return result, it.err +}
diff --git a/third_party/delve/dwarf/parseutil.go b/third_party/delve/dwarf/parseutil.go index d8ed2b0..a25f47f 100644 --- a/third_party/delve/dwarf/parseutil.go +++ b/third_party/delve/dwarf/parseutil.go
@@ -131,7 +131,7 @@ switch unitType { case _DW_UT_compile, _DW_UT_partial: - headerSize = 5 + secoffsz + headerSize = 4 + secoffsz case _DW_UT_skeleton, _DW_UT_split_compile: headerSize = 4 + secoffsz + 8