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:
parent
8f80f3314d
commit
4e83473154
@ -84,8 +84,13 @@ func Parse(compdir string, buf *bytes.Buffer, logfn func(string, ...interface{})
|
||||
dbl.normalizeBackslash = normalizeBackslash
|
||||
|
||||
parseDebugLinePrologue(dbl, buf)
|
||||
parseIncludeDirs(dbl, buf)
|
||||
parseFileEntries(dbl, buf)
|
||||
if dbl.Prologue.Version >= 5 {
|
||||
parseIncludeDirs5(dbl, buf)
|
||||
parseFileEntries5(dbl, buf)
|
||||
} else {
|
||||
parseIncludeDirs2(dbl, buf)
|
||||
parseFileEntries2(dbl, buf)
|
||||
}
|
||||
|
||||
// Instructions size calculation breakdown:
|
||||
// - 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.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.MinInstrLength = uint8(buf.Next(1)[0])
|
||||
if p.Version == 4 {
|
||||
@ -119,7 +130,8 @@ func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
|
||||
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 {
|
||||
str, _ := util.ParseString(buf)
|
||||
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 {
|
||||
entry := readFileEntry(info, buf, true)
|
||||
if entry.Path == "" {
|
||||
@ -165,3 +203,43 @@ func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
144
pkg/dwarf/line/parse_util.go
Normal file
144
pkg/dwarf/line/parse_util.go
Normal file
@ -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) {
|
||||
i, _ := util.DecodeULEB128(buf)
|
||||
if i-1 < uint64(len(sm.dbl.FileNames)) {
|
||||
sm.file = sm.dbl.FileNames[i-1].Path
|
||||
if sm.dbl.Prologue.Version < 5 {
|
||||
// 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 {
|
||||
j := (i - 1) - uint64(len(sm.dbl.FileNames))
|
||||
j := i - uint64(len(sm.dbl.FileNames))
|
||||
if j < uint64(len(sm.definedFiles)) {
|
||||
sm.file = sm.definedFiles[j].Path
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user