dwarf/line: support DWARF version 5 (#2090)

DWARFv5 has a new format for the header, directory table and line table
of the debug_line section.
This commit is contained in:
Alessandro Arzilli 2020-07-21 22:39:09 +02:00 committed by GitHub
parent 8f80f3314d
commit 4e83473154
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 233 additions and 7 deletions

@ -84,8 +84,13 @@ func Parse(compdir string, buf *bytes.Buffer, logfn func(string, ...interface{})
dbl.normalizeBackslash = normalizeBackslash dbl.normalizeBackslash = normalizeBackslash
parseDebugLinePrologue(dbl, buf) parseDebugLinePrologue(dbl, buf)
parseIncludeDirs(dbl, buf) if dbl.Prologue.Version >= 5 {
parseFileEntries(dbl, buf) parseIncludeDirs5(dbl, buf)
parseFileEntries5(dbl, buf)
} else {
parseIncludeDirs2(dbl, buf)
parseFileEntries2(dbl, buf)
}
// Instructions size calculation breakdown: // Instructions size calculation breakdown:
// - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length. // - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
@ -101,6 +106,12 @@ func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
p.UnitLength = binary.LittleEndian.Uint32(buf.Next(4)) p.UnitLength = binary.LittleEndian.Uint32(buf.Next(4))
p.Version = binary.LittleEndian.Uint16(buf.Next(2)) p.Version = binary.LittleEndian.Uint16(buf.Next(2))
if p.Version >= 5 {
dbl.ptrSize = int(buf.Next(1)[0]) // address_size
dbl.ptrSize += int(buf.Next(1)[0]) // segment_selector_size
}
// Version 4 or earlier
p.Length = binary.LittleEndian.Uint32(buf.Next(4)) p.Length = binary.LittleEndian.Uint32(buf.Next(4))
p.MinInstrLength = uint8(buf.Next(1)[0]) p.MinInstrLength = uint8(buf.Next(1)[0])
if p.Version == 4 { if p.Version == 4 {
@ -119,7 +130,8 @@ func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
dbl.Prologue = p dbl.Prologue = p
} }
func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) { // parseIncludeDirs2 parses the directory table for DWARF version 2 through 4.
func parseIncludeDirs2(info *DebugLineInfo, buf *bytes.Buffer) {
for { for {
str, _ := util.ParseString(buf) str, _ := util.ParseString(buf)
if str == "" { if str == "" {
@ -130,7 +142,33 @@ func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) {
} }
} }
func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) { // parseIncludeDirs5 parses the directory table for DWARF version 5.
func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) {
dirEntryFormReader := readEntryFormat(buf, info.Logf)
dirCount, _ := util.DecodeULEB128(buf)
info.IncludeDirs = make([]string, 0, dirCount)
for i := uint64(0); i < dirCount; i++ {
dirEntryFormReader.reset()
for dirEntryFormReader.next(buf) {
switch dirEntryFormReader.contentType {
case _DW_LNCT_path:
if dirEntryFormReader.formCode != _DW_FORM_string {
info.IncludeDirs = append(info.IncludeDirs, dirEntryFormReader.str)
} else {
//TODO(aarzilli): support debug_string, debug_line_str
info.Logf("unsupported string form %#x", dirEntryFormReader.formCode)
}
case _DW_LNCT_directory_index:
case _DW_LNCT_timestamp:
case _DW_LNCT_size:
case _DW_LNCT_MD5:
}
}
}
}
// parseFileEntries2 parses the file table for DWARF 2 through 4
func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) {
for { for {
entry := readFileEntry(info, buf, true) entry := readFileEntry(info, buf, true)
if entry.Path == "" { if entry.Path == "" {
@ -165,3 +203,43 @@ func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool)
return entry return entry
} }
// parseFileEntries5 parses the file table for DWARF 5
func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) {
fileEntryFormReader := readEntryFormat(buf, info.Logf)
fileCount, _ := util.DecodeULEB128(buf)
info.FileNames = make([]*FileEntry, 0, fileCount)
for i := 0; i < int(fileCount); i++ {
fileEntryFormReader.reset()
for fileEntryFormReader.next(buf) {
entry := new(FileEntry)
var path string
var diridx int = -1
switch fileEntryFormReader.contentType {
case _DW_LNCT_path:
if fileEntryFormReader.formCode != _DW_FORM_string {
path = fileEntryFormReader.str
} else {
//TODO(aarzilli): support debug_string, debug_line_str
info.Logf("unsupported string form %#x", fileEntryFormReader.formCode)
}
case _DW_LNCT_directory_index:
diridx = int(fileEntryFormReader.u64)
case _DW_LNCT_timestamp:
entry.LastModTime = fileEntryFormReader.u64
case _DW_LNCT_size:
entry.Length = fileEntryFormReader.u64
case _DW_LNCT_MD5:
// not implemented
}
if diridx >= 0 && !filepath.IsAbs(path) && diridx < len(info.IncludeDirs) {
path = filepath.Join(info.IncludeDirs[diridx], path)
}
entry.Path = path
info.FileNames = append(info.FileNames, entry)
info.Lookup[entry.Path] = entry
}
}
}

@ -0,0 +1,144 @@
package line
import (
"bytes"
"encoding/binary"
"github.com/go-delve/delve/pkg/dwarf/util"
)
const (
_DW_FORM_block = 0x09
_DW_FORM_block1 = 0x0a
_DW_FORM_block2 = 0x03
_DW_FORM_block4 = 0x04
_DW_FORM_data1 = 0x0b
_DW_FORM_data2 = 0x05
_DW_FORM_data4 = 0x06
_DW_FORM_data8 = 0x07
_DW_FORM_data16 = 0x1e
_DW_FORM_flag = 0x0c
_DW_FORM_line_strp = 0x1f
_DW_FORM_sdata = 0x0d
_DW_FORM_sec_offset = 0x17
_DW_FORM_string = 0x08
_DW_FORM_strp = 0x0e
_DW_FORM_strx = 0x1a
_DW_FORM_strx1 = 0x25
_DW_FORM_strx2 = 0x26
_DW_FORM_strx3 = 0x27
_DW_FORM_strx4 = 0x28
_DW_FORM_udata = 0x0f
)
const (
_DW_LNCT_path = 0x1 + iota
_DW_LNCT_directory_index
_DW_LNCT_timestamp
_DW_LNCT_size
_DW_LNCT_MD5
)
type formReader struct {
logf func(string, ...interface{})
contentTypes []uint64
formCodes []uint64
contentType uint64
formCode uint64
block []byte
u64 uint64
i64 int64
str string
nexti int
}
func readEntryFormat(buf *bytes.Buffer, logf func(string, ...interface{})) *formReader {
count := buf.Next(1)[0]
r := &formReader{
logf: logf,
contentTypes: make([]uint64, count),
formCodes: make([]uint64, count),
}
for i := range r.contentTypes {
r.contentTypes[i], _ = util.DecodeULEB128(buf)
r.formCodes[i], _ = util.DecodeULEB128(buf)
}
return r
}
func (rdr *formReader) reset() {
rdr.nexti = 0
}
func (rdr *formReader) next(buf *bytes.Buffer) bool {
if rdr.nexti >= len(rdr.contentTypes) {
return false
}
rdr.contentType = rdr.contentTypes[rdr.nexti]
rdr.formCode = rdr.formCodes[rdr.nexti]
switch rdr.formCode {
case _DW_FORM_block:
n, _ := util.DecodeULEB128(buf)
rdr.readBlock(buf, n)
case _DW_FORM_block1:
rdr.readBlock(buf, uint64(buf.Next(1)[0]))
case _DW_FORM_block2:
rdr.readBlock(buf, uint64(binary.LittleEndian.Uint16(buf.Next(2))))
case _DW_FORM_block4:
rdr.readBlock(buf, uint64(binary.LittleEndian.Uint32(buf.Next(4))))
case _DW_FORM_data1, _DW_FORM_flag, _DW_FORM_strx1:
rdr.u64 = uint64(buf.Next(1)[0])
case _DW_FORM_data2, _DW_FORM_strx2:
rdr.u64 = uint64(binary.LittleEndian.Uint16(buf.Next(2)))
case _DW_FORM_data4, _DW_FORM_line_strp, _DW_FORM_sec_offset, _DW_FORM_strp, _DW_FORM_strx4:
rdr.u64 = uint64(binary.LittleEndian.Uint32(buf.Next(4)))
case _DW_FORM_data8:
rdr.u64 = binary.LittleEndian.Uint64(buf.Next(8))
case _DW_FORM_data16:
rdr.readBlock(buf, 16)
case _DW_FORM_sdata:
rdr.i64, _ = util.DecodeSLEB128(buf)
case _DW_FORM_udata, _DW_FORM_strx:
rdr.u64, _ = util.DecodeULEB128(buf)
case _DW_FORM_string:
rdr.str, _ = util.ParseString(buf)
case _DW_FORM_strx3:
rdr.u64 = uint64(binary.LittleEndian.Uint32(append(buf.Next(3), 0x0)))
default:
if rdr.logf != nil {
rdr.logf("unknown form code %#x", rdr.formCode)
}
rdr.formCodes[rdr.nexti] = ^uint64(0) // only print error once
case ^uint64(0):
// do nothing
}
rdr.nexti++
return true
}
func (rdr *formReader) readBlock(buf *bytes.Buffer, n uint64) {
if cap(rdr.block) < int(n) {
rdr.block = make([]byte, 0, n)
}
rdr.block = rdr.block[:n]
buf.Read(rdr.block)
}

@ -501,10 +501,14 @@ func advanceline(sm *StateMachine, buf *bytes.Buffer) {
func setfile(sm *StateMachine, buf *bytes.Buffer) { func setfile(sm *StateMachine, buf *bytes.Buffer) {
i, _ := util.DecodeULEB128(buf) i, _ := util.DecodeULEB128(buf)
if i-1 < uint64(len(sm.dbl.FileNames)) { if sm.dbl.Prologue.Version < 5 {
sm.file = sm.dbl.FileNames[i-1].Path // in DWARF v5 files are indexed starting from 0, in v4 and prior the index starts at 1
i--
}
if i < uint64(len(sm.dbl.FileNames)) {
sm.file = sm.dbl.FileNames[i].Path
} else { } else {
j := (i - 1) - uint64(len(sm.dbl.FileNames)) j := i - uint64(len(sm.dbl.FileNames))
if j < uint64(len(sm.definedFiles)) { if j < uint64(len(sm.definedFiles)) {
sm.file = sm.definedFiles[j].Path sm.file = sm.definedFiles[j].Path
} else { } else {