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

     1  // Package frame contains data structures and
     2  // related functions for parsing and searching
     3  // through Dwarf .debug_frame data.
     4  package frame
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"io"
    11  
    12  	"github.com/undoio/delve/pkg/dwarf/util"
    13  )
    14  
    15  type parsefunc func(*parseContext) parsefunc
    16  
    17  type parseContext struct {
    18  	staticBase uint64
    19  
    20  	buf         *bytes.Buffer
    21  	totalLen    int
    22  	entries     FrameDescriptionEntries
    23  	ciemap      map[int]*CommonInformationEntry
    24  	common      *CommonInformationEntry
    25  	frame       *FrameDescriptionEntry
    26  	length      uint32
    27  	ptrSize     int
    28  	ehFrameAddr uint64
    29  	err         error
    30  }
    31  
    32  // Parse takes in data (a byte slice) and returns FrameDescriptionEntries,
    33  // which is a slice of FrameDescriptionEntry. Each FrameDescriptionEntry
    34  // has a pointer to CommonInformationEntry.
    35  // If ehFrameAddr is not zero the .eh_frame format will be used, a minor variant of DWARF described at https://www.airs.com/blog/archives/460.
    36  // The value of ehFrameAddr will be used as the address at which eh_frame will be mapped into memory
    37  func Parse(data []byte, order binary.ByteOrder, staticBase uint64, ptrSize int, ehFrameAddr uint64) (FrameDescriptionEntries, error) {
    38  	var (
    39  		buf  = bytes.NewBuffer(data)
    40  		pctx = &parseContext{buf: buf, totalLen: len(data), entries: newFrameIndex(), staticBase: staticBase, ptrSize: ptrSize, ehFrameAddr: ehFrameAddr, ciemap: map[int]*CommonInformationEntry{}}
    41  	)
    42  
    43  	for fn := parselength; buf.Len() != 0; {
    44  		fn = fn(pctx)
    45  		if pctx.err != nil {
    46  			return nil, pctx.err
    47  		}
    48  	}
    49  
    50  	for i := range pctx.entries {
    51  		pctx.entries[i].order = order
    52  	}
    53  
    54  	return pctx.entries, nil
    55  }
    56  
    57  func (ctx *parseContext) parsingEHFrame() bool {
    58  	return ctx.ehFrameAddr > 0
    59  }
    60  
    61  func (ctx *parseContext) cieEntry(cieid uint32) bool {
    62  	if ctx.parsingEHFrame() {
    63  		return cieid == 0x00
    64  	}
    65  	return cieid == 0xffffffff
    66  }
    67  
    68  func (ctx *parseContext) offset() int {
    69  	return ctx.totalLen - ctx.buf.Len()
    70  }
    71  
    72  func parselength(ctx *parseContext) parsefunc {
    73  	start := ctx.offset()
    74  	binary.Read(ctx.buf, binary.LittleEndian, &ctx.length) //TODO(aarzilli): this does not support 64bit DWARF
    75  
    76  	if ctx.length == 0 {
    77  		// ZERO terminator
    78  		return parselength
    79  	}
    80  
    81  	var cieid uint32
    82  	binary.Read(ctx.buf, binary.LittleEndian, &cieid)
    83  
    84  	ctx.length -= 4 // take off the length of the CIE id / CIE pointer.
    85  
    86  	if ctx.cieEntry(cieid) {
    87  		ctx.common = &CommonInformationEntry{Length: ctx.length, staticBase: ctx.staticBase, CIE_id: cieid}
    88  		ctx.ciemap[start] = ctx.common
    89  		return parseCIE
    90  	}
    91  
    92  	if ctx.ehFrameAddr > 0 {
    93  		cieid = uint32(start - int(cieid) + 4)
    94  	}
    95  
    96  	common := ctx.ciemap[int(cieid)]
    97  
    98  	if common == nil {
    99  		ctx.err = fmt.Errorf("unknown CIE_id %#x at %#x", cieid, start)
   100  	}
   101  
   102  	ctx.frame = &FrameDescriptionEntry{Length: ctx.length, CIE: common}
   103  	return parseFDE
   104  }
   105  
   106  func parseFDE(ctx *parseContext) parsefunc {
   107  	startOff := ctx.offset()
   108  	r := ctx.buf.Next(int(ctx.length))
   109  
   110  	reader := bytes.NewReader(r)
   111  	num := ctx.readEncodedPtr(addrSum(ctx.ehFrameAddr+uint64(startOff), reader), reader, ctx.frame.CIE.ptrEncAddr)
   112  	ctx.frame.begin = num + ctx.staticBase
   113  
   114  	// For the size field in .eh_frame only the size encoding portion of the
   115  	// address pointer encoding is considered.
   116  	// See decode_frame_entry_1 in gdb/dwarf2-frame.c.
   117  	// For .debug_frame ptrEncAddr is always ptrEncAbs and never has flags.
   118  	sizePtrEnc := ctx.frame.CIE.ptrEncAddr & 0x0f
   119  	ctx.frame.size = ctx.readEncodedPtr(0, reader, sizePtrEnc)
   120  
   121  	// Insert into the tree after setting address range begin
   122  	// otherwise compares won't work.
   123  	ctx.entries = append(ctx.entries, ctx.frame)
   124  
   125  	if ctx.parsingEHFrame() && len(ctx.frame.CIE.Augmentation) > 0 {
   126  		// If we are parsing a .eh_frame and we saw an agumentation string then we
   127  		// need to read the augmentation data, which are encoded as a ULEB128
   128  		// size followed by 'size' bytes.
   129  		n, _ := util.DecodeULEB128(reader)
   130  		reader.Seek(int64(n), io.SeekCurrent)
   131  	}
   132  
   133  	// The rest of this entry consists of the instructions
   134  	// so we can just grab all of the data from the buffer
   135  	// cursor to length.
   136  
   137  	off, _ := reader.Seek(0, io.SeekCurrent)
   138  	ctx.frame.Instructions = r[off:]
   139  	ctx.length = 0
   140  
   141  	return parselength
   142  }
   143  
   144  func addrSum(base uint64, buf *bytes.Reader) uint64 {
   145  	n, _ := buf.Seek(0, io.SeekCurrent)
   146  	return base + uint64(n)
   147  }
   148  
   149  func parseCIE(ctx *parseContext) parsefunc {
   150  	data := ctx.buf.Next(int(ctx.length))
   151  	buf := bytes.NewBuffer(data)
   152  	// parse version
   153  	ctx.common.Version, _ = buf.ReadByte()
   154  
   155  	// parse augmentation
   156  	ctx.common.Augmentation, _ = util.ParseString(buf)
   157  
   158  	if ctx.parsingEHFrame() {
   159  		if ctx.common.Augmentation == "eh" {
   160  			ctx.err = fmt.Errorf("unsupported 'eh' augmentation at %#x", ctx.offset())
   161  		}
   162  		if len(ctx.common.Augmentation) > 0 && ctx.common.Augmentation[0] != 'z' {
   163  			ctx.err = fmt.Errorf("unsupported augmentation at %#x (does not start with 'z')", ctx.offset())
   164  		}
   165  	}
   166  
   167  	// parse code alignment factor
   168  	ctx.common.CodeAlignmentFactor, _ = util.DecodeULEB128(buf)
   169  
   170  	// parse data alignment factor
   171  	ctx.common.DataAlignmentFactor, _ = util.DecodeSLEB128(buf)
   172  
   173  	// parse return address register
   174  	if ctx.parsingEHFrame() && ctx.common.Version == 1 {
   175  		b, _ := buf.ReadByte()
   176  		ctx.common.ReturnAddressRegister = uint64(b)
   177  	} else {
   178  		ctx.common.ReturnAddressRegister, _ = util.DecodeULEB128(buf)
   179  	}
   180  
   181  	ctx.common.ptrEncAddr = ptrEncAbs
   182  
   183  	if ctx.parsingEHFrame() && len(ctx.common.Augmentation) > 0 {
   184  		_, _ = util.DecodeULEB128(buf) // augmentation data length
   185  		for i := 1; i < len(ctx.common.Augmentation); i++ {
   186  			switch ctx.common.Augmentation[i] {
   187  			case 'L':
   188  				_, _ = buf.ReadByte() // LSDA pointer encoding, we don't support this.
   189  			case 'R':
   190  				// Pointer encoding, describes how begin and size fields of FDEs are encoded.
   191  				b, _ := buf.ReadByte()
   192  				ctx.common.ptrEncAddr = ptrEnc(b)
   193  				if !ctx.common.ptrEncAddr.Supported() {
   194  					ctx.err = fmt.Errorf("pointer encoding not supported %#x at %#x", ctx.common.ptrEncAddr, ctx.offset())
   195  					return nil
   196  				}
   197  			case 'S':
   198  				// Signal handler invocation frame, we don't support this but there is no associated data to read.
   199  			case 'P':
   200  				// Personality function encoded as a pointer encoding byte followed by
   201  				// the pointer to the personality function encoded as specified by the
   202  				// pointer encoding.
   203  				// We don't support this but have to read it anyway.
   204  				b, _ := buf.ReadByte()
   205  				e := ptrEnc(b) &^ ptrEncIndirect
   206  				if !e.Supported() {
   207  					ctx.err = fmt.Errorf("pointer encoding not supported %#x at %#x", e, ctx.offset())
   208  					return nil
   209  				}
   210  				ctx.readEncodedPtr(0, buf, e)
   211  			default:
   212  				ctx.err = fmt.Errorf("unsupported augmentation character %c at %#x", ctx.common.Augmentation[i], ctx.offset())
   213  				return nil
   214  			}
   215  		}
   216  	}
   217  
   218  	// parse initial instructions
   219  	// The rest of this entry consists of the instructions
   220  	// so we can just grab all of the data from the buffer
   221  	// cursor to length.
   222  	ctx.common.InitialInstructions = buf.Bytes() //ctx.buf.Next(int(ctx.length))
   223  	ctx.length = 0
   224  
   225  	return parselength
   226  }
   227  
   228  // readEncodedPtr reads a pointer from buf encoded as specified by ptrEnc.
   229  // This function is used to read pointers from a .eh_frame section, when
   230  // used to parse a .debug_frame section ptrEnc will always be ptrEncAbs.
   231  // The parameter addr is the address that the current byte of 'buf' will be
   232  // mapped to when the executable file containing the eh_frame section being
   233  // parse is loaded in memory.
   234  func (ctx *parseContext) readEncodedPtr(addr uint64, buf util.ByteReaderWithLen, ptrEnc ptrEnc) uint64 {
   235  	if ptrEnc == ptrEncOmit {
   236  		return 0
   237  	}
   238  
   239  	var ptr uint64
   240  
   241  	switch ptrEnc & 0xf {
   242  	case ptrEncAbs, ptrEncSigned:
   243  		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, ctx.ptrSize)
   244  	case ptrEncUleb:
   245  		ptr, _ = util.DecodeULEB128(buf)
   246  	case ptrEncUdata2:
   247  		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 2)
   248  	case ptrEncSdata2:
   249  		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 2)
   250  		ptr = uint64(int16(ptr))
   251  	case ptrEncUdata4:
   252  		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 4)
   253  	case ptrEncSdata4:
   254  		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 4)
   255  		ptr = uint64(int32(ptr))
   256  	case ptrEncUdata8, ptrEncSdata8:
   257  		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 8)
   258  	case ptrEncSleb:
   259  		n, _ := util.DecodeSLEB128(buf)
   260  		ptr = uint64(n)
   261  	}
   262  
   263  	if ptrEnc&0xf0 == ptrEncPCRel {
   264  		ptr += addr
   265  	}
   266  
   267  	return ptr
   268  }
   269  
   270  // DwarfEndian determines the endianness of the DWARF by using the version number field in the debug_info section
   271  // Trick borrowed from "debug/dwarf".New()
   272  func DwarfEndian(infoSec []byte) binary.ByteOrder {
   273  	if len(infoSec) < 6 {
   274  		return binary.BigEndian
   275  	}
   276  	x, y := infoSec[4], infoSec[5]
   277  	switch {
   278  	case x == 0 && y == 0:
   279  		return binary.BigEndian
   280  	case x == 0:
   281  		return binary.BigEndian
   282  	case y == 0:
   283  		return binary.LittleEndian
   284  	default:
   285  		return binary.BigEndian
   286  	}
   287  }