delve/pkg/dwarf/parseutil.go

151 lines
3.2 KiB
Go

package dwarf
import (
"bytes"
"debug/dwarf"
"encoding/binary"
"fmt"
"io"
)
// ReadString reads a null-terminated string from data.
func ReadString(data *bytes.Buffer) (string, error) {
str, err := data.ReadString(0x0)
if err != nil {
return "", err
}
return str[:len(str)-1], nil
}
// ReadUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader.
func ReadUintRaw(reader io.Reader, order binary.ByteOrder, ptrSize int) (uint64, error) {
switch ptrSize {
case 2:
var n uint16
if err := binary.Read(reader, order, &n); err != nil {
return 0, err
}
return uint64(n), nil
case 4:
var n uint32
if err := binary.Read(reader, order, &n); err != nil {
return 0, err
}
return uint64(n), nil
case 8:
var n uint64
if err := binary.Read(reader, order, &n); err != nil {
return 0, err
}
return n, nil
}
return 0, fmt.Errorf("pointer size %d not supported", ptrSize)
}
// WriteUint writes an integer of ptrSize bytes to writer, in the specified byte order.
func WriteUint(writer io.Writer, order binary.ByteOrder, ptrSize int, data uint64) error {
switch ptrSize {
case 4:
return binary.Write(writer, order, uint32(data))
case 8:
return binary.Write(writer, order, data)
}
return fmt.Errorf("pointer size %d not supported", ptrSize)
}
// ReadDwarfLengthVersion reads a DWARF length field followed by a version field
func ReadDwarfLengthVersion(data []byte) (length uint64, dwarf64 bool, version uint8, byteOrder binary.ByteOrder) {
if len(data) < 4 {
return 0, false, 0, binary.LittleEndian
}
lengthfield := binary.LittleEndian.Uint32(data)
voff := 4
if lengthfield == ^uint32(0) {
dwarf64 = true
voff = 12
}
if voff+1 >= len(data) {
return 0, false, 0, binary.LittleEndian
}
byteOrder = binary.LittleEndian
x, y := data[voff], data[voff+1]
switch {
default:
fallthrough
case x == 0 && y == 0:
version = 0
byteOrder = binary.LittleEndian
case x == 0:
version = y
byteOrder = binary.BigEndian
case y == 0:
version = x
byteOrder = binary.LittleEndian
}
if dwarf64 {
length = byteOrder.Uint64(data[4:])
} else {
length = uint64(byteOrder.Uint32(data))
}
return length, dwarf64, version, byteOrder
}
const (
_DW_UT_compile = 0x1 + iota
_DW_UT_type
_DW_UT_partial
_DW_UT_skeleton
_DW_UT_split_compile
_DW_UT_split_type
)
// ReadUnitVersions reads the DWARF version of each unit in a debug_info section and returns them as a map.
func ReadUnitVersions(data []byte) map[dwarf.Offset]uint8 {
r := make(map[dwarf.Offset]uint8)
off := dwarf.Offset(0)
for len(data) > 0 {
length, dwarf64, version, _ := ReadDwarfLengthVersion(data)
data = data[4:]
off += 4
secoffsz := 4
if dwarf64 {
off += 8
secoffsz = 8
data = data[8:]
}
var headerSize int
switch version {
case 2, 3, 4:
headerSize = 3 + secoffsz
default: // 5 and later?
unitType := data[2]
switch unitType {
case _DW_UT_compile, _DW_UT_partial:
headerSize = 5 + secoffsz
case _DW_UT_skeleton, _DW_UT_split_compile:
headerSize = 4 + secoffsz + 8
case _DW_UT_type, _DW_UT_split_type:
headerSize = 4 + secoffsz + 8 + secoffsz
}
}
r[off+dwarf.Offset(headerSize)] = version
data = data[length:] // skip contents
off += dwarf.Offset(length)
}
return r
}