github.com/undoio/delve@v1.9.0/pkg/dwarf/util/util.go (about)

     1  package util
     2  
     3  import (
     4  	"bytes"
     5  	"debug/dwarf"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"io"
     9  )
    10  
    11  // ByteReaderWithLen is a io.ByteReader with a Len method. This interface is
    12  // satisified by both bytes.Buffer and bytes.Reader.
    13  type ByteReaderWithLen interface {
    14  	io.ByteReader
    15  	io.Reader
    16  	Len() int
    17  }
    18  
    19  // The Little Endian Base 128 format is defined in the DWARF v4 standard,
    20  // section 7.6, page 161 and following.
    21  
    22  // DecodeULEB128 decodes an unsigned Little Endian Base 128
    23  // represented number.
    24  func DecodeULEB128(buf ByteReaderWithLen) (uint64, uint32) {
    25  	var (
    26  		result uint64
    27  		shift  uint64
    28  		length uint32
    29  	)
    30  
    31  	if buf.Len() == 0 {
    32  		return 0, 0
    33  	}
    34  
    35  	for {
    36  		b, err := buf.ReadByte()
    37  		if err != nil {
    38  			panic("Could not parse ULEB128 value")
    39  		}
    40  		length++
    41  
    42  		result |= uint64((uint(b) & 0x7f) << shift)
    43  
    44  		// If high order bit is 1.
    45  		if b&0x80 == 0 {
    46  			break
    47  		}
    48  
    49  		shift += 7
    50  	}
    51  
    52  	return result, length
    53  }
    54  
    55  // DecodeSLEB128 decodes a signed Little Endian Base 128
    56  // represented number.
    57  func DecodeSLEB128(buf ByteReaderWithLen) (int64, uint32) {
    58  	var (
    59  		b      byte
    60  		err    error
    61  		result int64
    62  		shift  uint64
    63  		length uint32
    64  	)
    65  
    66  	if buf.Len() == 0 {
    67  		return 0, 0
    68  	}
    69  
    70  	for {
    71  		b, err = buf.ReadByte()
    72  		if err != nil {
    73  			panic("Could not parse SLEB128 value")
    74  		}
    75  		length++
    76  
    77  		result |= int64((int64(b) & 0x7f) << shift)
    78  		shift += 7
    79  		if b&0x80 == 0 {
    80  			break
    81  		}
    82  	}
    83  
    84  	if (shift < 8*uint64(length)) && (b&0x40 > 0) {
    85  		result |= -(1 << shift)
    86  	}
    87  
    88  	return result, length
    89  }
    90  
    91  // EncodeULEB128 encodes x to the unsigned Little Endian Base 128 format
    92  // into out.
    93  func EncodeULEB128(out io.ByteWriter, x uint64) {
    94  	for {
    95  		b := byte(x & 0x7f)
    96  		x = x >> 7
    97  		if x != 0 {
    98  			b = b | 0x80
    99  		}
   100  		out.WriteByte(b)
   101  		if x == 0 {
   102  			break
   103  		}
   104  	}
   105  }
   106  
   107  // EncodeSLEB128 encodes x to the signed Little Endian Base 128 format
   108  // into out.
   109  func EncodeSLEB128(out io.ByteWriter, x int64) {
   110  	for {
   111  		b := byte(x & 0x7f)
   112  		x >>= 7
   113  
   114  		signb := b & 0x40
   115  
   116  		last := false
   117  		if (x == 0 && signb == 0) || (x == -1 && signb != 0) {
   118  			last = true
   119  		} else {
   120  			b = b | 0x80
   121  		}
   122  		out.WriteByte(b)
   123  
   124  		if last {
   125  			break
   126  		}
   127  	}
   128  }
   129  
   130  // ParseString reads a null-terminated string from data.
   131  func ParseString(data *bytes.Buffer) (string, error) {
   132  	str, err := data.ReadString(0x0)
   133  	if err != nil {
   134  		return "", err
   135  	}
   136  
   137  	return str[:len(str)-1], nil
   138  }
   139  
   140  // ReadUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader.
   141  func ReadUintRaw(reader io.Reader, order binary.ByteOrder, ptrSize int) (uint64, error) {
   142  	switch ptrSize {
   143  	case 2:
   144  		var n uint16
   145  		if err := binary.Read(reader, order, &n); err != nil {
   146  			return 0, err
   147  		}
   148  		return uint64(n), nil
   149  	case 4:
   150  		var n uint32
   151  		if err := binary.Read(reader, order, &n); err != nil {
   152  			return 0, err
   153  		}
   154  		return uint64(n), nil
   155  	case 8:
   156  		var n uint64
   157  		if err := binary.Read(reader, order, &n); err != nil {
   158  			return 0, err
   159  		}
   160  		return n, nil
   161  	}
   162  	return 0, fmt.Errorf("pointer size %d not supported", ptrSize)
   163  }
   164  
   165  // WriteUint writes an integer of ptrSize bytes to writer, in the specified byte order.
   166  func WriteUint(writer io.Writer, order binary.ByteOrder, ptrSize int, data uint64) error {
   167  	switch ptrSize {
   168  	case 4:
   169  		return binary.Write(writer, order, uint32(data))
   170  	case 8:
   171  		return binary.Write(writer, order, data)
   172  	}
   173  	return fmt.Errorf("pointer size %d not supported", ptrSize)
   174  }
   175  
   176  // ReadDwarfLengthVersion reads a DWARF length field followed by a version field
   177  func ReadDwarfLengthVersion(data []byte) (length uint64, dwarf64 bool, version uint8, byteOrder binary.ByteOrder) {
   178  	if len(data) < 4 {
   179  		return 0, false, 0, binary.LittleEndian
   180  	}
   181  
   182  	lengthfield := binary.LittleEndian.Uint32(data)
   183  	voff := 4
   184  	if lengthfield == ^uint32(0) {
   185  		dwarf64 = true
   186  		voff = 12
   187  	}
   188  
   189  	if voff+1 >= len(data) {
   190  		return 0, false, 0, binary.LittleEndian
   191  	}
   192  
   193  	byteOrder = binary.LittleEndian
   194  	x, y := data[voff], data[voff+1]
   195  	switch {
   196  	default:
   197  		fallthrough
   198  	case x == 0 && y == 0:
   199  		version = 0
   200  		byteOrder = binary.LittleEndian
   201  	case x == 0:
   202  		version = y
   203  		byteOrder = binary.BigEndian
   204  	case y == 0:
   205  		version = x
   206  		byteOrder = binary.LittleEndian
   207  	}
   208  
   209  	if dwarf64 {
   210  		length = byteOrder.Uint64(data[4:])
   211  	} else {
   212  		length = uint64(byteOrder.Uint32(data))
   213  	}
   214  
   215  	return length, dwarf64, version, byteOrder
   216  }
   217  
   218  const (
   219  	_DW_UT_compile = 0x1 + iota
   220  	_DW_UT_type
   221  	_DW_UT_partial
   222  	_DW_UT_skeleton
   223  	_DW_UT_split_compile
   224  	_DW_UT_split_type
   225  )
   226  
   227  // ReadUnitVersions reads the DWARF version of each unit in a debug_info section and returns them as a map.
   228  func ReadUnitVersions(data []byte) map[dwarf.Offset]uint8 {
   229  	r := make(map[dwarf.Offset]uint8)
   230  	off := dwarf.Offset(0)
   231  	for len(data) > 0 {
   232  		length, dwarf64, version, _ := ReadDwarfLengthVersion(data)
   233  
   234  		data = data[4:]
   235  		off += 4
   236  		secoffsz := 4
   237  		if dwarf64 {
   238  			off += 8
   239  			secoffsz = 8
   240  			data = data[8:]
   241  		}
   242  
   243  		var headerSize int
   244  
   245  		switch version {
   246  		case 2, 3, 4:
   247  			headerSize = 3 + secoffsz
   248  		default: // 5 and later?
   249  			unitType := data[2]
   250  
   251  			switch unitType {
   252  			case _DW_UT_compile, _DW_UT_partial:
   253  				headerSize = 5 + secoffsz
   254  
   255  			case _DW_UT_skeleton, _DW_UT_split_compile:
   256  				headerSize = 4 + secoffsz + 8
   257  
   258  			case _DW_UT_type, _DW_UT_split_type:
   259  				headerSize = 4 + secoffsz + 8 + secoffsz
   260  			}
   261  		}
   262  
   263  		r[off+dwarf.Offset(headerSize)] = version
   264  
   265  		data = data[length:] // skip contents
   266  		off += dwarf.Offset(length)
   267  	}
   268  	return r
   269  }