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

     1  package op
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  
    10  	"github.com/undoio/delve/pkg/dwarf/util"
    11  )
    12  
    13  // Opcode represent a DWARF stack program instruction.
    14  // See ./opcodes.go for a full list.
    15  type Opcode byte
    16  
    17  //go:generate go run ../../../_scripts/gen-opcodes.go opcodes.table opcodes.go
    18  
    19  type stackfn func(Opcode, *context) error
    20  
    21  type ReadMemoryFunc func([]byte, uint64) (int, error)
    22  
    23  type context struct {
    24  	buf     *bytes.Buffer
    25  	prog    []byte
    26  	stack   []int64
    27  	pieces  []Piece
    28  	ptrSize int
    29  
    30  	DwarfRegisters
    31  	readMemory ReadMemoryFunc
    32  }
    33  
    34  // Piece is a piece of memory stored either at an address or in a register.
    35  type Piece struct {
    36  	Size  int
    37  	Kind  PieceKind
    38  	Val   uint64
    39  	Bytes []byte
    40  }
    41  
    42  // PieceKind describes the kind of a piece.
    43  type PieceKind uint8
    44  
    45  const (
    46  	AddrPiece PieceKind = iota // The piece is stored in memory, Val is the address
    47  	RegPiece                   // The piece is stored in a register, Val is the register number
    48  	ImmPiece                   // The piece is an immediate value, Val or Bytes is the value
    49  )
    50  
    51  var (
    52  	ErrStackUnderflow        = errors.New("DWARF stack underflow")
    53  	ErrStackIndexOutOfBounds = errors.New("DWARF stack index out of bounds")
    54  	ErrMemoryReadUnavailable = errors.New("memory read unavailable")
    55  )
    56  
    57  const arbitraryExecutionLimitFactor = 10
    58  
    59  // ExecuteStackProgram executes a DWARF location expression and returns
    60  // either an address (int64), or a slice of Pieces for location expressions
    61  // that don't evaluate to an address (such as register and composite expressions).
    62  func ExecuteStackProgram(regs DwarfRegisters, instructions []byte, ptrSize int, readMemory ReadMemoryFunc) (int64, []Piece, error) {
    63  	ctxt := &context{
    64  		buf:            bytes.NewBuffer(instructions),
    65  		prog:           instructions,
    66  		stack:          make([]int64, 0, 3),
    67  		DwarfRegisters: regs,
    68  		ptrSize:        ptrSize,
    69  	}
    70  
    71  	for tick := 0; tick < len(instructions)*arbitraryExecutionLimitFactor; tick++ {
    72  		opcodeByte, err := ctxt.buf.ReadByte()
    73  		if err != nil {
    74  			break
    75  		}
    76  		opcode := Opcode(opcodeByte)
    77  		if opcode == DW_OP_nop {
    78  			continue
    79  		}
    80  		fn, ok := oplut[opcode]
    81  		if !ok {
    82  			return 0, nil, fmt.Errorf("invalid instruction %#v", opcode)
    83  		}
    84  
    85  		err = fn(opcode, ctxt)
    86  		if err != nil {
    87  			return 0, nil, err
    88  		}
    89  	}
    90  
    91  	if ctxt.pieces != nil {
    92  		if len(ctxt.pieces) == 1 && ctxt.pieces[0].Kind == RegPiece {
    93  			return int64(regs.Uint64Val(ctxt.pieces[0].Val)), ctxt.pieces, nil
    94  		}
    95  		return 0, ctxt.pieces, nil
    96  	}
    97  
    98  	if len(ctxt.stack) == 0 {
    99  		return 0, nil, errors.New("empty OP stack")
   100  	}
   101  
   102  	return ctxt.stack[len(ctxt.stack)-1], nil, nil
   103  }
   104  
   105  // PrettyPrint prints the DWARF stack program instructions to `out`.
   106  func PrettyPrint(out io.Writer, instructions []byte) {
   107  	in := bytes.NewBuffer(instructions)
   108  
   109  	for {
   110  		opcode, err := in.ReadByte()
   111  		if err != nil {
   112  			break
   113  		}
   114  		if name, hasname := opcodeName[Opcode(opcode)]; hasname {
   115  			io.WriteString(out, name)
   116  			out.Write([]byte{' '})
   117  		} else {
   118  			fmt.Fprintf(out, "%#x ", opcode)
   119  		}
   120  		for _, arg := range opcodeArgs[Opcode(opcode)] {
   121  			switch arg {
   122  			case 's':
   123  				n, _ := util.DecodeSLEB128(in)
   124  				fmt.Fprintf(out, "%#x ", n)
   125  			case 'u':
   126  				n, _ := util.DecodeULEB128(in)
   127  				fmt.Fprintf(out, "%#x ", n)
   128  			case '1':
   129  				var x uint8
   130  				binary.Read(in, binary.LittleEndian, &x)
   131  				fmt.Fprintf(out, "%#x ", x)
   132  			case '2':
   133  				var x uint16
   134  				binary.Read(in, binary.LittleEndian, &x)
   135  				fmt.Fprintf(out, "%#x ", x)
   136  			case '4':
   137  				var x uint32
   138  				binary.Read(in, binary.LittleEndian, &x)
   139  				fmt.Fprintf(out, "%#x ", x)
   140  			case '8':
   141  				var x uint64
   142  				binary.Read(in, binary.LittleEndian, &x)
   143  				fmt.Fprintf(out, "%#x ", x)
   144  			case 'B':
   145  				sz, _ := util.DecodeULEB128(in)
   146  				data := make([]byte, sz)
   147  				sz2, _ := in.Read(data)
   148  				data = data[:sz2]
   149  				fmt.Fprintf(out, "%d [%x] ", sz, data)
   150  			}
   151  		}
   152  	}
   153  }
   154  
   155  // closeLoc is called by opcodes that can only appear at the end of a
   156  // location expression (DW_OP_regN, DW_OP_regx, DW_OP_stack_value...).
   157  // It checks that we are at the end of the program or that the following
   158  // opcode is DW_OP_piece or DW_OP_bit_piece and processes them.
   159  func (ctxt *context) closeLoc(opcode0 Opcode, piece Piece) error {
   160  	if ctxt.buf.Len() == 0 {
   161  		ctxt.pieces = append(ctxt.pieces, piece)
   162  		return nil
   163  	}
   164  
   165  	// DWARF doesn't say what happens to the operand stack at the end of a
   166  	// location expression, resetting it here.
   167  	ctxt.stack = ctxt.stack[:0]
   168  
   169  	b, err := ctxt.buf.ReadByte()
   170  	if err != nil {
   171  		return err
   172  	}
   173  	opcode := Opcode(b)
   174  
   175  	switch opcode {
   176  	case DW_OP_piece:
   177  		sz, _ := util.DecodeULEB128(ctxt.buf)
   178  		piece.Size = int(sz)
   179  		ctxt.pieces = append(ctxt.pieces, piece)
   180  		return nil
   181  
   182  	case DW_OP_bit_piece:
   183  		// not supported
   184  		return fmt.Errorf("invalid instruction %#v", opcode)
   185  	default:
   186  		return fmt.Errorf("invalid instruction %#v after %#v", opcode, opcode0)
   187  	}
   188  }
   189  
   190  func callframecfa(opcode Opcode, ctxt *context) error {
   191  	if ctxt.CFA == 0 {
   192  		return errors.New("could not retrieve CFA for current PC")
   193  	}
   194  	ctxt.stack = append(ctxt.stack, int64(ctxt.CFA))
   195  	return nil
   196  }
   197  
   198  func addr(opcode Opcode, ctxt *context) error {
   199  	buf := ctxt.buf.Next(ctxt.ptrSize)
   200  	stack, err := util.ReadUintRaw(bytes.NewReader(buf), binary.LittleEndian, ctxt.ptrSize)
   201  	if err != nil {
   202  		return err
   203  	}
   204  	ctxt.stack = append(ctxt.stack, int64(stack+ctxt.StaticBase))
   205  	return nil
   206  }
   207  
   208  func plusuconsts(opcode Opcode, ctxt *context) error {
   209  	slen := len(ctxt.stack)
   210  	num, _ := util.DecodeULEB128(ctxt.buf)
   211  	ctxt.stack[slen-1] = ctxt.stack[slen-1] + int64(num)
   212  	return nil
   213  }
   214  
   215  func consts(opcode Opcode, ctxt *context) error {
   216  	num, _ := util.DecodeSLEB128(ctxt.buf)
   217  	ctxt.stack = append(ctxt.stack, num)
   218  	return nil
   219  }
   220  
   221  func framebase(opcode Opcode, ctxt *context) error {
   222  	num, _ := util.DecodeSLEB128(ctxt.buf)
   223  	ctxt.stack = append(ctxt.stack, ctxt.FrameBase+num)
   224  	return nil
   225  }
   226  
   227  func register(opcode Opcode, ctxt *context) error {
   228  	var regnum uint64
   229  	if opcode == DW_OP_regx {
   230  		regnum, _ = util.DecodeULEB128(ctxt.buf)
   231  	} else {
   232  		regnum = uint64(opcode - DW_OP_reg0)
   233  	}
   234  	return ctxt.closeLoc(opcode, Piece{Kind: RegPiece, Val: regnum})
   235  }
   236  
   237  func bregister(opcode Opcode, ctxt *context) error {
   238  	var regnum uint64
   239  	if opcode == DW_OP_bregx {
   240  		regnum, _ = util.DecodeULEB128(ctxt.buf)
   241  	} else {
   242  		regnum = uint64(opcode - DW_OP_breg0)
   243  	}
   244  	offset, _ := util.DecodeSLEB128(ctxt.buf)
   245  	if ctxt.Reg(regnum) == nil {
   246  		return fmt.Errorf("register %d not available", regnum)
   247  	}
   248  	ctxt.stack = append(ctxt.stack, int64(ctxt.Uint64Val(regnum))+offset)
   249  	return nil
   250  }
   251  
   252  func piece(opcode Opcode, ctxt *context) error {
   253  	sz, _ := util.DecodeULEB128(ctxt.buf)
   254  
   255  	if len(ctxt.stack) == 0 {
   256  		// nothing on the stack means this piece is unavailable (padding,
   257  		// optimized away...), see DWARFv4 sec. 2.6.1.3 page 30.
   258  		ctxt.pieces = append(ctxt.pieces, Piece{Size: int(sz), Kind: ImmPiece, Val: 0})
   259  		return nil
   260  	}
   261  
   262  	addr := ctxt.stack[len(ctxt.stack)-1]
   263  	ctxt.pieces = append(ctxt.pieces, Piece{Size: int(sz), Kind: AddrPiece, Val: uint64(addr)})
   264  	ctxt.stack = ctxt.stack[:0]
   265  	return nil
   266  }
   267  
   268  func literal(opcode Opcode, ctxt *context) error {
   269  	ctxt.stack = append(ctxt.stack, int64(opcode-DW_OP_lit0))
   270  	return nil
   271  }
   272  
   273  func constnu(opcode Opcode, ctxt *context) error {
   274  	var (
   275  		n   uint64
   276  		err error
   277  	)
   278  	switch opcode {
   279  	case DW_OP_const1u:
   280  		var b uint8
   281  		b, err = ctxt.buf.ReadByte()
   282  		n = uint64(b)
   283  	case DW_OP_const2u:
   284  		n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 2)
   285  	case DW_OP_const4u:
   286  		n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 4)
   287  	case DW_OP_const8u:
   288  		n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 8)
   289  	default:
   290  		panic("internal error")
   291  	}
   292  	if err != nil {
   293  		return err
   294  	}
   295  	ctxt.stack = append(ctxt.stack, int64(n))
   296  	return nil
   297  }
   298  
   299  func constns(opcode Opcode, ctxt *context) error {
   300  	var (
   301  		n   uint64
   302  		err error
   303  	)
   304  	switch opcode {
   305  	case DW_OP_const1s:
   306  		var b uint8
   307  		b, err = ctxt.buf.ReadByte()
   308  		n = uint64(int64(int8(b)))
   309  	case DW_OP_const2s:
   310  		n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 2)
   311  		n = uint64(int64(int16(n)))
   312  	case DW_OP_const4s:
   313  		n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 4)
   314  		n = uint64(int64(int32(n)))
   315  	case DW_OP_const8s:
   316  		n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 8)
   317  	default:
   318  		panic("internal error")
   319  	}
   320  	if err != nil {
   321  		return err
   322  	}
   323  	ctxt.stack = append(ctxt.stack, int64(n))
   324  	return nil
   325  }
   326  
   327  func constu(opcode Opcode, ctxt *context) error {
   328  	num, _ := util.DecodeULEB128(ctxt.buf)
   329  	ctxt.stack = append(ctxt.stack, int64(num))
   330  	return nil
   331  }
   332  
   333  func dup(_ Opcode, ctxt *context) error {
   334  	if len(ctxt.stack) <= 0 {
   335  		return ErrStackUnderflow
   336  	}
   337  	ctxt.stack = append(ctxt.stack, ctxt.stack[len(ctxt.stack)-1])
   338  	return nil
   339  }
   340  
   341  func drop(_ Opcode, ctxt *context) error {
   342  	if len(ctxt.stack) <= 0 {
   343  		return ErrStackUnderflow
   344  	}
   345  	ctxt.stack = ctxt.stack[:len(ctxt.stack)-1]
   346  	return nil
   347  }
   348  
   349  func pick(opcode Opcode, ctxt *context) error {
   350  	var n byte
   351  	switch opcode {
   352  	case DW_OP_pick:
   353  		n, _ = ctxt.buf.ReadByte()
   354  	case DW_OP_over:
   355  		n = 1
   356  	default:
   357  		panic("internal error")
   358  	}
   359  	idx := len(ctxt.stack) - 1 - int(uint8(n))
   360  	if idx < 0 || idx >= len(ctxt.stack) {
   361  		return ErrStackIndexOutOfBounds
   362  	}
   363  	ctxt.stack = append(ctxt.stack, ctxt.stack[idx])
   364  	return nil
   365  }
   366  
   367  func swap(_ Opcode, ctxt *context) error {
   368  	if len(ctxt.stack) < 2 {
   369  		return ErrStackUnderflow
   370  	}
   371  	ctxt.stack[len(ctxt.stack)-1], ctxt.stack[len(ctxt.stack)-2] = ctxt.stack[len(ctxt.stack)-2], ctxt.stack[len(ctxt.stack)-1]
   372  	return nil
   373  }
   374  
   375  func rot(_ Opcode, ctxt *context) error {
   376  	if len(ctxt.stack) < 3 {
   377  		return ErrStackUnderflow
   378  	}
   379  	ctxt.stack[len(ctxt.stack)-1], ctxt.stack[len(ctxt.stack)-2], ctxt.stack[len(ctxt.stack)-3] = ctxt.stack[len(ctxt.stack)-2], ctxt.stack[len(ctxt.stack)-3], ctxt.stack[len(ctxt.stack)-1]
   380  	return nil
   381  }
   382  
   383  func unaryop(opcode Opcode, ctxt *context) error {
   384  	if len(ctxt.stack) < 1 {
   385  		return ErrStackUnderflow
   386  	}
   387  	operand := ctxt.stack[len(ctxt.stack)-1]
   388  	switch opcode {
   389  	case DW_OP_abs:
   390  		if operand < 0 {
   391  			operand = -operand
   392  		}
   393  	case DW_OP_neg:
   394  		operand = -operand
   395  	case DW_OP_not:
   396  		operand = ^operand
   397  	default:
   398  		panic("internal error")
   399  	}
   400  	ctxt.stack[len(ctxt.stack)-1] = operand
   401  	return nil
   402  }
   403  
   404  func binaryop(opcode Opcode, ctxt *context) error {
   405  	if len(ctxt.stack) < 2 {
   406  		return ErrStackUnderflow
   407  	}
   408  	second := ctxt.stack[len(ctxt.stack)-2]
   409  	top := ctxt.stack[len(ctxt.stack)-1]
   410  	var r int64
   411  	ctxt.stack = ctxt.stack[:len(ctxt.stack)-2]
   412  	switch opcode {
   413  	case DW_OP_and:
   414  		r = second & top
   415  	case DW_OP_div:
   416  		r = second / top
   417  	case DW_OP_minus:
   418  		r = second - top
   419  	case DW_OP_mod:
   420  		r = second % top
   421  	case DW_OP_mul:
   422  		r = second * top
   423  	case DW_OP_or:
   424  		r = second | top
   425  	case DW_OP_plus:
   426  		r = second + top
   427  	case DW_OP_shl:
   428  		r = second << uint64(top)
   429  	case DW_OP_shr:
   430  		r = second >> uint64(top)
   431  	case DW_OP_shra:
   432  		r = int64(uint64(second) >> uint64(top))
   433  	case DW_OP_xor:
   434  		r = second ^ top
   435  	case DW_OP_le:
   436  		r = bool2int(second <= top)
   437  	case DW_OP_ge:
   438  		r = bool2int(second >= top)
   439  	case DW_OP_eq:
   440  		r = bool2int(second == top)
   441  	case DW_OP_lt:
   442  		r = bool2int(second < top)
   443  	case DW_OP_gt:
   444  		r = bool2int(second > top)
   445  	case DW_OP_ne:
   446  		r = bool2int(second != top)
   447  	default:
   448  		panic("internal error")
   449  	}
   450  	ctxt.stack = append(ctxt.stack, r)
   451  	return nil
   452  }
   453  
   454  func bool2int(b bool) int64 {
   455  	if b {
   456  		return 1
   457  	}
   458  	return 0
   459  }
   460  
   461  func (ctxt *context) jump(n int16) error {
   462  	i := len(ctxt.prog) - ctxt.buf.Len() + int(n)
   463  	if i < 0 {
   464  		return ErrStackUnderflow
   465  	}
   466  	if i >= len(ctxt.prog) {
   467  		i = len(ctxt.prog)
   468  	}
   469  	ctxt.buf = bytes.NewBuffer(ctxt.prog[i:])
   470  	return nil
   471  }
   472  
   473  func skip(_ Opcode, ctxt *context) error {
   474  	var n int16
   475  	binary.Read(ctxt.buf, binary.LittleEndian, &n)
   476  	return ctxt.jump(n)
   477  }
   478  
   479  func bra(_ Opcode, ctxt *context) error {
   480  	var n int16
   481  	binary.Read(ctxt.buf, binary.LittleEndian, &n)
   482  
   483  	if len(ctxt.stack) < 1 {
   484  		return ErrStackUnderflow
   485  	}
   486  	top := ctxt.stack[len(ctxt.stack)-1]
   487  	ctxt.stack = ctxt.stack[:len(ctxt.stack)-1]
   488  	if top != 0 {
   489  		return ctxt.jump(n)
   490  	}
   491  	return nil
   492  }
   493  
   494  func stackvalue(_ Opcode, ctxt *context) error {
   495  	if len(ctxt.stack) < 1 {
   496  		return ErrStackUnderflow
   497  	}
   498  	val := ctxt.stack[len(ctxt.stack)-1]
   499  	ctxt.stack = ctxt.stack[:len(ctxt.stack)-1]
   500  	return ctxt.closeLoc(DW_OP_stack_value, Piece{Kind: ImmPiece, Val: uint64(val)})
   501  }
   502  
   503  func implicitvalue(_ Opcode, ctxt *context) error {
   504  	sz, _ := util.DecodeULEB128(ctxt.buf)
   505  	block := make([]byte, sz)
   506  	n, _ := ctxt.buf.Read(block)
   507  	if uint64(n) != sz {
   508  		return fmt.Errorf("insufficient bytes read while reading DW_OP_implicit_value's block %d (expected: %d)", n, sz)
   509  	}
   510  	return ctxt.closeLoc(DW_OP_implicit_value, Piece{Kind: ImmPiece, Bytes: block, Size: int(sz)})
   511  }
   512  
   513  func deref(op Opcode, ctxt *context) error {
   514  	if ctxt.readMemory == nil {
   515  		return ErrMemoryReadUnavailable
   516  	}
   517  
   518  	sz := ctxt.ptrSize
   519  	if op == DW_OP_deref_size || op == DW_OP_xderef_size {
   520  		n, err := ctxt.buf.ReadByte()
   521  		if err != nil {
   522  			return err
   523  		}
   524  		sz = int(n)
   525  	}
   526  
   527  	if len(ctxt.stack) <= 0 {
   528  		return ErrStackUnderflow
   529  	}
   530  
   531  	addr := ctxt.stack[len(ctxt.stack)-1]
   532  	ctxt.stack = ctxt.stack[:len(ctxt.stack)-1]
   533  
   534  	if op == DW_OP_xderef || op == DW_OP_xderef_size {
   535  		if len(ctxt.stack) <= 0 {
   536  			return ErrStackUnderflow
   537  		}
   538  		// the second element on the stack is the "address space identifier" which we don't do anything with
   539  		ctxt.stack = ctxt.stack[:len(ctxt.stack)-1]
   540  	}
   541  
   542  	buf := make([]byte, sz)
   543  	_, err := ctxt.readMemory(buf, uint64(addr))
   544  	if err != nil {
   545  		return err
   546  	}
   547  
   548  	x, err := util.ReadUintRaw(bytes.NewReader(buf), binary.LittleEndian, sz)
   549  	if err != nil {
   550  		return err
   551  	}
   552  
   553  	ctxt.stack = append(ctxt.stack, int64(x))
   554  
   555  	return nil
   556  }