mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-04 06:32:16 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			289 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			289 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Package frame contains data structures and
 | 
						|
// related functions for parsing and searching
 | 
						|
// through Dwarf .debug_frame data.
 | 
						|
package frame
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/binary"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
 | 
						|
	"github.com/go-delve/delve/pkg/dwarf"
 | 
						|
	"github.com/go-delve/delve/pkg/dwarf/leb128"
 | 
						|
)
 | 
						|
 | 
						|
type parsefunc func(*parseContext) parsefunc
 | 
						|
 | 
						|
type parseContext struct {
 | 
						|
	staticBase uint64
 | 
						|
 | 
						|
	buf         *bytes.Buffer
 | 
						|
	totalLen    int
 | 
						|
	entries     FrameDescriptionEntries
 | 
						|
	ciemap      map[int]*CommonInformationEntry
 | 
						|
	common      *CommonInformationEntry
 | 
						|
	frame       *FrameDescriptionEntry
 | 
						|
	length      uint32
 | 
						|
	ptrSize     int
 | 
						|
	ehFrameAddr uint64
 | 
						|
	err         error
 | 
						|
}
 | 
						|
 | 
						|
// Parse takes in data (a byte slice) and returns FrameDescriptionEntries,
 | 
						|
// which is a slice of FrameDescriptionEntry. Each FrameDescriptionEntry
 | 
						|
// has a pointer to CommonInformationEntry.
 | 
						|
// 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.
 | 
						|
// The value of ehFrameAddr will be used as the address at which eh_frame will be mapped into memory
 | 
						|
func Parse(data []byte, order binary.ByteOrder, staticBase uint64, ptrSize int, ehFrameAddr uint64) (FrameDescriptionEntries, error) {
 | 
						|
	var (
 | 
						|
		buf  = bytes.NewBuffer(data)
 | 
						|
		pctx = &parseContext{buf: buf, totalLen: len(data), entries: newFrameIndex(), staticBase: staticBase, ptrSize: ptrSize, ehFrameAddr: ehFrameAddr, ciemap: map[int]*CommonInformationEntry{}}
 | 
						|
	)
 | 
						|
 | 
						|
	for fn := parselength; buf.Len() != 0; {
 | 
						|
		fn = fn(pctx)
 | 
						|
		if pctx.err != nil {
 | 
						|
			return nil, pctx.err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for i := range pctx.entries {
 | 
						|
		pctx.entries[i].order = order
 | 
						|
	}
 | 
						|
 | 
						|
	return pctx.entries, nil
 | 
						|
}
 | 
						|
 | 
						|
func (ctx *parseContext) parsingEHFrame() bool {
 | 
						|
	return ctx.ehFrameAddr > 0
 | 
						|
}
 | 
						|
 | 
						|
func (ctx *parseContext) cieEntry(cieid uint32) bool {
 | 
						|
	if ctx.parsingEHFrame() {
 | 
						|
		return cieid == 0x00
 | 
						|
	}
 | 
						|
	return cieid == 0xffffffff
 | 
						|
}
 | 
						|
 | 
						|
func (ctx *parseContext) offset() int {
 | 
						|
	return ctx.totalLen - ctx.buf.Len()
 | 
						|
}
 | 
						|
 | 
						|
func parselength(ctx *parseContext) parsefunc {
 | 
						|
	start := ctx.offset()
 | 
						|
	binary.Read(ctx.buf, binary.LittleEndian, &ctx.length) //TODO(aarzilli): this does not support 64bit DWARF
 | 
						|
 | 
						|
	if ctx.length == 0 {
 | 
						|
		// ZERO terminator
 | 
						|
		return parselength
 | 
						|
	}
 | 
						|
 | 
						|
	var cieid uint32
 | 
						|
	binary.Read(ctx.buf, binary.LittleEndian, &cieid)
 | 
						|
 | 
						|
	ctx.length -= 4 // take off the length of the CIE id / CIE pointer.
 | 
						|
 | 
						|
	if ctx.cieEntry(cieid) {
 | 
						|
		ctx.common = &CommonInformationEntry{Length: ctx.length, staticBase: ctx.staticBase, CIE_id: cieid}
 | 
						|
		ctx.ciemap[start] = ctx.common
 | 
						|
		return parseCIE
 | 
						|
	}
 | 
						|
 | 
						|
	if ctx.ehFrameAddr > 0 {
 | 
						|
		cieid = uint32(start - int(cieid) + 4)
 | 
						|
	}
 | 
						|
 | 
						|
	common := ctx.ciemap[int(cieid)]
 | 
						|
 | 
						|
	if common == nil {
 | 
						|
		ctx.err = fmt.Errorf("unknown CIE_id %#x at %#x", cieid, start)
 | 
						|
	}
 | 
						|
 | 
						|
	ctx.frame = &FrameDescriptionEntry{Length: ctx.length, CIE: common}
 | 
						|
	return parseFDE
 | 
						|
}
 | 
						|
 | 
						|
func parseFDE(ctx *parseContext) parsefunc {
 | 
						|
	startOff := ctx.offset()
 | 
						|
	r := ctx.buf.Next(int(ctx.length))
 | 
						|
 | 
						|
	reader := bytes.NewReader(r)
 | 
						|
	num := ctx.readEncodedPtr(addrSum(ctx.ehFrameAddr+uint64(startOff), reader), reader, ctx.frame.CIE.ptrEncAddr)
 | 
						|
	ctx.frame.begin = num + ctx.staticBase
 | 
						|
 | 
						|
	// For the size field in .eh_frame only the size encoding portion of the
 | 
						|
	// address pointer encoding is considered.
 | 
						|
	// See decode_frame_entry_1 in gdb/dwarf2-frame.c.
 | 
						|
	// For .debug_frame ptrEncAddr is always ptrEncAbs and never has flags.
 | 
						|
	sizePtrEnc := ctx.frame.CIE.ptrEncAddr & 0x0f
 | 
						|
	ctx.frame.size = ctx.readEncodedPtr(0, reader, sizePtrEnc)
 | 
						|
 | 
						|
	// Insert into the tree after setting address range begin
 | 
						|
	// otherwise compares won't work.
 | 
						|
	ctx.entries = append(ctx.entries, ctx.frame)
 | 
						|
 | 
						|
	if ctx.parsingEHFrame() && len(ctx.frame.CIE.Augmentation) > 0 {
 | 
						|
		// If we are parsing a .eh_frame and we saw an augmentation string then we
 | 
						|
		// need to read the augmentation data, which are encoded as a ULEB128
 | 
						|
		// size followed by 'size' bytes.
 | 
						|
		n, _ := leb128.DecodeUnsigned(reader)
 | 
						|
		reader.Seek(int64(n), io.SeekCurrent)
 | 
						|
	}
 | 
						|
 | 
						|
	// The rest of this entry consists of the instructions
 | 
						|
	// so we can just grab all of the data from the buffer
 | 
						|
	// cursor to length.
 | 
						|
 | 
						|
	off, _ := reader.Seek(0, io.SeekCurrent)
 | 
						|
	ctx.frame.Instructions = r[off:]
 | 
						|
	ctx.length = 0
 | 
						|
 | 
						|
	return parselength
 | 
						|
}
 | 
						|
 | 
						|
func addrSum(base uint64, buf *bytes.Reader) uint64 {
 | 
						|
	n, _ := buf.Seek(0, io.SeekCurrent)
 | 
						|
	return base + uint64(n)
 | 
						|
}
 | 
						|
 | 
						|
func parseCIE(ctx *parseContext) parsefunc {
 | 
						|
	data := ctx.buf.Next(int(ctx.length))
 | 
						|
	buf := bytes.NewBuffer(data)
 | 
						|
	// parse version
 | 
						|
	ctx.common.Version, _ = buf.ReadByte()
 | 
						|
 | 
						|
	// parse augmentation
 | 
						|
	ctx.common.Augmentation, _ = dwarf.ReadString(buf)
 | 
						|
 | 
						|
	if ctx.parsingEHFrame() {
 | 
						|
		if ctx.common.Augmentation == "eh" {
 | 
						|
			ctx.err = fmt.Errorf("unsupported 'eh' augmentation at %#x", ctx.offset())
 | 
						|
		}
 | 
						|
		if len(ctx.common.Augmentation) > 0 && ctx.common.Augmentation[0] != 'z' {
 | 
						|
			ctx.err = fmt.Errorf("unsupported augmentation at %#x (does not start with 'z')", ctx.offset())
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// parse code alignment factor
 | 
						|
	ctx.common.CodeAlignmentFactor, _ = leb128.DecodeUnsigned(buf)
 | 
						|
 | 
						|
	// parse data alignment factor
 | 
						|
	ctx.common.DataAlignmentFactor, _ = leb128.DecodeSigned(buf)
 | 
						|
 | 
						|
	// parse return address register
 | 
						|
	if ctx.parsingEHFrame() && ctx.common.Version == 1 {
 | 
						|
		b, _ := buf.ReadByte()
 | 
						|
		ctx.common.ReturnAddressRegister = uint64(b)
 | 
						|
	} else {
 | 
						|
		ctx.common.ReturnAddressRegister, _ = leb128.DecodeUnsigned(buf)
 | 
						|
	}
 | 
						|
 | 
						|
	ctx.common.ptrEncAddr = ptrEncAbs
 | 
						|
 | 
						|
	if ctx.parsingEHFrame() && len(ctx.common.Augmentation) > 0 {
 | 
						|
		_, _ = leb128.DecodeUnsigned(buf) // augmentation data length
 | 
						|
		for i := 1; i < len(ctx.common.Augmentation); i++ {
 | 
						|
			switch ctx.common.Augmentation[i] {
 | 
						|
			case 'L':
 | 
						|
				_, _ = buf.ReadByte() // LSDA pointer encoding, we don't support this.
 | 
						|
			case 'R':
 | 
						|
				// Pointer encoding, describes how begin and size fields of FDEs are encoded.
 | 
						|
				b, _ := buf.ReadByte()
 | 
						|
				ctx.common.ptrEncAddr = ptrEnc(b)
 | 
						|
				if !ctx.common.ptrEncAddr.Supported() {
 | 
						|
					ctx.err = fmt.Errorf("pointer encoding not supported %#x at %#x", ctx.common.ptrEncAddr, ctx.offset())
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
			case 'S':
 | 
						|
				// Signal handler invocation frame, we don't support this but there is no associated data to read.
 | 
						|
			case 'P':
 | 
						|
				// Personality function encoded as a pointer encoding byte followed by
 | 
						|
				// the pointer to the personality function encoded as specified by the
 | 
						|
				// pointer encoding.
 | 
						|
				// We don't support this but have to read it anyway.
 | 
						|
				b, _ := buf.ReadByte()
 | 
						|
				e := ptrEnc(b) &^ ptrEncIndirect
 | 
						|
				if !e.Supported() {
 | 
						|
					ctx.err = fmt.Errorf("pointer encoding not supported %#x at %#x", e, ctx.offset())
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
				ctx.readEncodedPtr(0, buf, e)
 | 
						|
			default:
 | 
						|
				ctx.err = fmt.Errorf("unsupported augmentation character %c at %#x", ctx.common.Augmentation[i], ctx.offset())
 | 
						|
				return nil
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// parse initial instructions
 | 
						|
	// The rest of this entry consists of the instructions
 | 
						|
	// so we can just grab all of the data from the buffer
 | 
						|
	// cursor to length.
 | 
						|
	ctx.common.InitialInstructions = buf.Bytes() //ctx.buf.Next(int(ctx.length))
 | 
						|
	ctx.length = 0
 | 
						|
 | 
						|
	return parselength
 | 
						|
}
 | 
						|
 | 
						|
// readEncodedPtr reads a pointer from buf encoded as specified by ptrEnc.
 | 
						|
// This function is used to read pointers from a .eh_frame section, when
 | 
						|
// used to parse a .debug_frame section ptrEnc will always be ptrEncAbs.
 | 
						|
// The parameter addr is the address that the current byte of 'buf' will be
 | 
						|
// mapped to when the executable file containing the eh_frame section being
 | 
						|
// parse is loaded in memory.
 | 
						|
func (ctx *parseContext) readEncodedPtr(addr uint64, buf leb128.Reader, ptrEnc ptrEnc) uint64 {
 | 
						|
	if ptrEnc == ptrEncOmit {
 | 
						|
		return 0
 | 
						|
	}
 | 
						|
 | 
						|
	var ptr uint64
 | 
						|
 | 
						|
	switch ptrEnc & 0xf {
 | 
						|
	case ptrEncAbs, ptrEncSigned:
 | 
						|
		ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, ctx.ptrSize)
 | 
						|
	case ptrEncUleb:
 | 
						|
		ptr, _ = leb128.DecodeUnsigned(buf)
 | 
						|
	case ptrEncUdata2:
 | 
						|
		ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 2)
 | 
						|
	case ptrEncSdata2:
 | 
						|
		ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 2)
 | 
						|
		ptr = uint64(int16(ptr))
 | 
						|
	case ptrEncUdata4:
 | 
						|
		ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 4)
 | 
						|
	case ptrEncSdata4:
 | 
						|
		ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 4)
 | 
						|
		ptr = uint64(int32(ptr))
 | 
						|
	case ptrEncUdata8, ptrEncSdata8:
 | 
						|
		ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 8)
 | 
						|
	case ptrEncSleb:
 | 
						|
		n, _ := leb128.DecodeSigned(buf)
 | 
						|
		ptr = uint64(n)
 | 
						|
	}
 | 
						|
 | 
						|
	if ptrEnc&0xf0 == ptrEncPCRel {
 | 
						|
		ptr += addr
 | 
						|
	}
 | 
						|
 | 
						|
	return ptr
 | 
						|
}
 | 
						|
 | 
						|
// DwarfEndian determines the endianness of the DWARF by using the version number field in the debug_info section
 | 
						|
// Trick borrowed from "debug/dwarf".New()
 | 
						|
func DwarfEndian(infoSec []byte) binary.ByteOrder {
 | 
						|
	if len(infoSec) < 6 {
 | 
						|
		return binary.BigEndian
 | 
						|
	}
 | 
						|
	x, y := infoSec[4], infoSec[5]
 | 
						|
	switch {
 | 
						|
	case x == 0 && y == 0:
 | 
						|
		return binary.BigEndian
 | 
						|
	case x == 0:
 | 
						|
		return binary.BigEndian
 | 
						|
	case y == 0:
 | 
						|
		return binary.LittleEndian
 | 
						|
	default:
 | 
						|
		return binary.BigEndian
 | 
						|
	}
 | 
						|
}
 |