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