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