diff --git a/pkg/dwarf/op/op.go b/pkg/dwarf/op/op.go index 19c72874..ee37b162 100644 --- a/pkg/dwarf/op/op.go +++ b/pkg/dwarf/op/op.go @@ -18,21 +18,25 @@ type Opcode byte type stackfn func(Opcode, *context) error +type ReadMemoryFunc func([]byte, uint64) (int, error) + type context struct { buf *bytes.Buffer + prog []byte stack []int64 pieces []Piece - reg bool ptrSize int DwarfRegisters + readMemory ReadMemoryFunc } // Piece is a piece of memory stored either at an address or in a register. type Piece struct { - Size int - Kind PieceKind - Val uint64 + Size int + Kind PieceKind + Val uint64 + Bytes []byte } // PieceKind describes the kind of a piece. @@ -41,32 +45,37 @@ type PieceKind uint8 const ( AddrPiece PieceKind = iota // The piece is stored in memory, Val is the address RegPiece // The piece is stored in a register, Val is the register number - ImmPiece // The piece is an immediate value, Val is the value + ImmPiece // The piece is an immediate value, Val or Bytes is the value ) +var ( + ErrStackUnderflow = errors.New("DWARF stack underflow") + ErrStackIndexOutOfBounds = errors.New("DWARF stack index out of bounds") + ErrMemoryReadUnavailable = errors.New("memory read unavailable") +) + +const arbitraryExecutionLimitFactor = 10 + // ExecuteStackProgram executes a DWARF location expression and returns // either an address (int64), or a slice of Pieces for location expressions // that don't evaluate to an address (such as register and composite expressions). -func ExecuteStackProgram(regs DwarfRegisters, instructions []byte, ptrSize int) (int64, []Piece, error) { +func ExecuteStackProgram(regs DwarfRegisters, instructions []byte, ptrSize int, readMemory ReadMemoryFunc) (int64, []Piece, error) { ctxt := &context{ buf: bytes.NewBuffer(instructions), + prog: instructions, stack: make([]int64, 0, 3), DwarfRegisters: regs, ptrSize: ptrSize, } - for { + for tick := 0; tick < len(instructions)*arbitraryExecutionLimitFactor; tick++ { opcodeByte, err := ctxt.buf.ReadByte() if err != nil { break } opcode := Opcode(opcodeByte) - if ctxt.reg && opcode != DW_OP_piece { - // last opcode was DW_OP_regN and next one isn't DW_OP_piece so convert - // the register piece into a stack value. - ctxt.stack = append(ctxt.stack, int64(regs.Uint64Val(ctxt.pieces[len(ctxt.pieces)-1].Val))) - ctxt.pieces = ctxt.pieces[:len(ctxt.pieces)-1] - ctxt.reg = false + if opcode == DW_OP_nop { + continue } fn, ok := oplut[opcode] if !ok { @@ -143,6 +152,41 @@ func PrettyPrint(out io.Writer, instructions []byte) { } } +// closeLoc is called by opcodes that can only appear at the end of a +// location expression (DW_OP_regN, DW_OP_regx, DW_OP_stack_value...). +// It checks that we are at the end of the program or that the following +// opcode is DW_OP_piece or DW_OP_bit_piece and processes them. +func (ctxt *context) closeLoc(opcode0 Opcode, piece Piece) error { + if ctxt.buf.Len() == 0 { + ctxt.pieces = append(ctxt.pieces, piece) + return nil + } + + // DWARF doesn't say what happens to the operand stack at the end of a + // location expression, resetting it here. + ctxt.stack = ctxt.stack[:0] + + b, err := ctxt.buf.ReadByte() + if err != nil { + return err + } + opcode := Opcode(b) + + switch opcode { + case DW_OP_piece: + sz, _ := util.DecodeULEB128(ctxt.buf) + piece.Size = int(sz) + ctxt.pieces = append(ctxt.pieces, piece) + return nil + + case DW_OP_bit_piece: + // not supported + return fmt.Errorf("invalid instruction %#v", opcode) + default: + return fmt.Errorf("invalid instruction %#v after %#v", opcode, opcode0) + } +} + func callframecfa(opcode Opcode, ctxt *context) error { if ctxt.CFA == 0 { return errors.New("could not retrieve CFA for current PC") @@ -161,17 +205,6 @@ func addr(opcode Opcode, ctxt *context) error { return nil } -func plus(opcode Opcode, ctxt *context) error { - var ( - slen = len(ctxt.stack) - digits = ctxt.stack[slen-2 : slen] - st = ctxt.stack[:slen-2] - ) - - ctxt.stack = append(st, digits[0]+digits[1]) - return nil -} - func plusuconsts(opcode Opcode, ctxt *context) error { slen := len(ctxt.stack) num, _ := util.DecodeULEB128(ctxt.buf) @@ -192,23 +225,32 @@ func framebase(opcode Opcode, ctxt *context) error { } func register(opcode Opcode, ctxt *context) error { - ctxt.reg = true + var regnum uint64 if opcode == DW_OP_regx { - n, _ := util.DecodeSLEB128(ctxt.buf) - ctxt.pieces = append(ctxt.pieces, Piece{Kind: RegPiece, Val: uint64(n)}) + regnum, _ = util.DecodeULEB128(ctxt.buf) } else { - ctxt.pieces = append(ctxt.pieces, Piece{Kind: RegPiece, Val: uint64(opcode - DW_OP_reg0)}) + regnum = uint64(opcode - DW_OP_reg0) } + return ctxt.closeLoc(opcode, Piece{Kind: RegPiece, Val: regnum}) +} + +func bregister(opcode Opcode, ctxt *context) error { + var regnum uint64 + if opcode == DW_OP_bregx { + regnum, _ = util.DecodeULEB128(ctxt.buf) + } else { + regnum = uint64(opcode - DW_OP_breg0) + } + offset, _ := util.DecodeSLEB128(ctxt.buf) + if ctxt.Reg(regnum) == nil { + return fmt.Errorf("register %d not available", regnum) + } + ctxt.stack = append(ctxt.stack, int64(ctxt.Uint64Val(regnum))+offset) return nil } func piece(opcode Opcode, ctxt *context) error { sz, _ := util.DecodeULEB128(ctxt.buf) - if ctxt.reg { - ctxt.reg = false - ctxt.pieces[len(ctxt.pieces)-1].Size = int(sz) - return nil - } if len(ctxt.stack) == 0 { // nothing on the stack means this piece is unavailable (padding, @@ -222,3 +264,293 @@ func piece(opcode Opcode, ctxt *context) error { ctxt.stack = ctxt.stack[:0] return nil } + +func literal(opcode Opcode, ctxt *context) error { + ctxt.stack = append(ctxt.stack, int64(opcode-DW_OP_lit0)) + return nil +} + +func constnu(opcode Opcode, ctxt *context) error { + var ( + n uint64 + err error + ) + switch opcode { + case DW_OP_const1u: + var b uint8 + b, err = ctxt.buf.ReadByte() + n = uint64(b) + case DW_OP_const2u: + n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 2) + case DW_OP_const4u: + n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 4) + case DW_OP_const8u: + n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 8) + default: + panic("internal error") + } + if err != nil { + return err + } + ctxt.stack = append(ctxt.stack, int64(n)) + return nil +} + +func constns(opcode Opcode, ctxt *context) error { + var ( + n uint64 + err error + ) + switch opcode { + case DW_OP_const1s: + var b uint8 + b, err = ctxt.buf.ReadByte() + n = uint64(int64(int8(b))) + case DW_OP_const2s: + n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 2) + n = uint64(int64(int16(n))) + case DW_OP_const4s: + n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 4) + n = uint64(int64(int32(n))) + case DW_OP_const8s: + n, err = util.ReadUintRaw(ctxt.buf, binary.LittleEndian, 8) + default: + panic("internal error") + } + if err != nil { + return err + } + ctxt.stack = append(ctxt.stack, int64(n)) + return nil +} + +func constu(opcode Opcode, ctxt *context) error { + num, _ := util.DecodeULEB128(ctxt.buf) + ctxt.stack = append(ctxt.stack, int64(num)) + return nil +} + +func dup(_ Opcode, ctxt *context) error { + if len(ctxt.stack) <= 0 { + return ErrStackUnderflow + } + ctxt.stack = append(ctxt.stack, ctxt.stack[len(ctxt.stack)-1]) + return nil +} + +func drop(_ Opcode, ctxt *context) error { + if len(ctxt.stack) <= 0 { + return ErrStackUnderflow + } + ctxt.stack = ctxt.stack[:len(ctxt.stack)-1] + return nil +} + +func pick(opcode Opcode, ctxt *context) error { + var n byte + switch opcode { + case DW_OP_pick: + n, _ = ctxt.buf.ReadByte() + case DW_OP_over: + n = 1 + default: + panic("internal error") + } + idx := len(ctxt.stack) - 1 - int(uint8(n)) + if idx < 0 || idx >= len(ctxt.stack) { + return ErrStackIndexOutOfBounds + } + ctxt.stack = append(ctxt.stack, ctxt.stack[idx]) + return nil +} + +func swap(_ Opcode, ctxt *context) error { + if len(ctxt.stack) < 2 { + return ErrStackUnderflow + } + ctxt.stack[len(ctxt.stack)-1], ctxt.stack[len(ctxt.stack)-2] = ctxt.stack[len(ctxt.stack)-2], ctxt.stack[len(ctxt.stack)-1] + return nil +} + +func rot(_ Opcode, ctxt *context) error { + if len(ctxt.stack) < 3 { + return ErrStackUnderflow + } + 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] + return nil +} + +func unaryop(opcode Opcode, ctxt *context) error { + if len(ctxt.stack) < 1 { + return ErrStackUnderflow + } + operand := ctxt.stack[len(ctxt.stack)-1] + switch opcode { + case DW_OP_abs: + if operand < 0 { + operand = -operand + } + case DW_OP_neg: + operand = -operand + case DW_OP_not: + operand = ^operand + default: + panic("internal error") + } + ctxt.stack[len(ctxt.stack)-1] = operand + return nil +} + +func binaryop(opcode Opcode, ctxt *context) error { + if len(ctxt.stack) < 2 { + return ErrStackUnderflow + } + second := ctxt.stack[len(ctxt.stack)-2] + top := ctxt.stack[len(ctxt.stack)-1] + var r int64 + ctxt.stack = ctxt.stack[:len(ctxt.stack)-2] + switch opcode { + case DW_OP_and: + r = second & top + case DW_OP_div: + r = second / top + case DW_OP_minus: + r = second - top + case DW_OP_mod: + r = second % top + case DW_OP_mul: + r = second * top + case DW_OP_or: + r = second | top + case DW_OP_plus: + r = second + top + case DW_OP_shl: + r = second << uint64(top) + case DW_OP_shr: + r = second >> uint64(top) + case DW_OP_shra: + r = int64(uint64(second) >> uint64(top)) + case DW_OP_xor: + r = second ^ top + case DW_OP_le: + r = bool2int(second <= top) + case DW_OP_ge: + r = bool2int(second >= top) + case DW_OP_eq: + r = bool2int(second == top) + case DW_OP_lt: + r = bool2int(second < top) + case DW_OP_gt: + r = bool2int(second > top) + case DW_OP_ne: + r = bool2int(second != top) + default: + panic("internal error") + } + ctxt.stack = append(ctxt.stack, r) + return nil +} + +func bool2int(b bool) int64 { + if b { + return 1 + } + return 0 +} + +func (ctxt *context) jump(n int16) error { + i := len(ctxt.prog) - ctxt.buf.Len() + int(n) + if i < 0 { + return ErrStackUnderflow + } + if i >= len(ctxt.prog) { + i = len(ctxt.prog) + } + ctxt.buf = bytes.NewBuffer(ctxt.prog[i:]) + return nil +} + +func skip(_ Opcode, ctxt *context) error { + var n int16 + binary.Read(ctxt.buf, binary.LittleEndian, &n) + return ctxt.jump(n) +} + +func bra(_ Opcode, ctxt *context) error { + var n int16 + binary.Read(ctxt.buf, binary.LittleEndian, &n) + + if len(ctxt.stack) < 1 { + return ErrStackUnderflow + } + top := ctxt.stack[len(ctxt.stack)-1] + ctxt.stack = ctxt.stack[:len(ctxt.stack)-1] + if top != 0 { + return ctxt.jump(n) + } + return nil +} + +func stackvalue(_ Opcode, ctxt *context) error { + if len(ctxt.stack) < 1 { + return ErrStackUnderflow + } + val := ctxt.stack[len(ctxt.stack)-1] + ctxt.stack = ctxt.stack[:len(ctxt.stack)-1] + return ctxt.closeLoc(DW_OP_stack_value, Piece{Kind: ImmPiece, Val: uint64(val)}) +} + +func implicitvalue(_ Opcode, ctxt *context) error { + sz, _ := util.DecodeULEB128(ctxt.buf) + block := make([]byte, sz) + n, _ := ctxt.buf.Read(block) + if uint64(n) != sz { + return fmt.Errorf("insufficient bytes read while reading DW_OP_implicit_value's block %d (expected: %d)", n, sz) + } + return ctxt.closeLoc(DW_OP_implicit_value, Piece{Kind: ImmPiece, Bytes: block, Size: int(sz)}) +} + +func deref(op Opcode, ctxt *context) error { + if ctxt.readMemory == nil { + return ErrMemoryReadUnavailable + } + + sz := ctxt.ptrSize + if op == DW_OP_deref_size || op == DW_OP_xderef_size { + n, err := ctxt.buf.ReadByte() + if err != nil { + return err + } + sz = int(n) + } + + if len(ctxt.stack) <= 0 { + return ErrStackUnderflow + } + + addr := ctxt.stack[len(ctxt.stack)-1] + ctxt.stack = ctxt.stack[:len(ctxt.stack)-1] + + if op == DW_OP_xderef || op == DW_OP_xderef_size { + if len(ctxt.stack) <= 0 { + return ErrStackUnderflow + } + // the second element on the stack is the "address space identifier" which we don't do anything with + ctxt.stack = ctxt.stack[:len(ctxt.stack)-1] + } + + buf := make([]byte, sz) + _, err := ctxt.readMemory(buf, uint64(addr)) + if err != nil { + return err + } + + x, err := util.ReadUintRaw(bytes.NewReader(buf), binary.LittleEndian, sz) + if err != nil { + return err + } + + ctxt.stack = append(ctxt.stack, int64(x)) + + return nil +} diff --git a/pkg/dwarf/op/op_test.go b/pkg/dwarf/op/op_test.go index 0a346261..1faab63e 100644 --- a/pkg/dwarf/op/op_test.go +++ b/pkg/dwarf/op/op_test.go @@ -1,25 +1,58 @@ package op import ( + "strings" "testing" - "unsafe" ) -func ptrSizeByRuntimeArch() int { - return int(unsafe.Sizeof(uintptr(0))) +func assertExprResult(t *testing.T, expected int64, instructions []byte) { + t.Helper() + actual, _, err := ExecuteStackProgram(DwarfRegisters{}, instructions, 8, nil) + if err != nil { + t.Error(err) + } + if actual != expected { + buf := new(strings.Builder) + PrettyPrint(buf, instructions) + t.Errorf("actual %d != expected %d (in %s)", actual, expected, buf.String()) + } } func TestExecuteStackProgram(t *testing.T) { - var ( - instructions = []byte{byte(DW_OP_consts), 0x1c, byte(DW_OP_consts), 0x1c, byte(DW_OP_plus)} - expected = int64(56) - ) - actual, _, err := ExecuteStackProgram(DwarfRegisters{}, instructions, ptrSizeByRuntimeArch()) - if err != nil { - t.Fatal(err) - } - - if actual != expected { - t.Fatalf("actual %d != expected %d", actual, expected) - } + assertExprResult(t, 56, []byte{byte(DW_OP_consts), 0x1c, byte(DW_OP_consts), 0x1c, byte(DW_OP_plus)}) +} + +func TestSignExtension(t *testing.T) { + var tgt uint64 = 0xffffffffffffff88 + assertExprResult(t, int64(tgt), []byte{byte(DW_OP_const1s), 0x88}) + tgt = 0xffffffffffff8888 + assertExprResult(t, int64(tgt), []byte{byte(DW_OP_const2s), 0x88, 0x88}) +} + +func TestStackOps(t *testing.T) { + assertExprResult(t, 1, []byte{byte(DW_OP_lit1), byte(DW_OP_lit2), byte(DW_OP_drop)}) + assertExprResult(t, 0, []byte{byte(DW_OP_lit1), byte(DW_OP_lit0), byte(DW_OP_pick), 0}) + assertExprResult(t, 1, []byte{byte(DW_OP_lit1), byte(DW_OP_lit0), byte(DW_OP_pick), 1}) +} + +func TestBra(t *testing.T) { + assertExprResult(t, 32, []byte{ + byte(DW_OP_lit1), + byte(DW_OP_lit5), + + byte(DW_OP_dup), + byte(DW_OP_lit0), + byte(DW_OP_eq), + byte(DW_OP_bra), 9, 0x0, + + byte(DW_OP_swap), + byte(DW_OP_dup), + byte(DW_OP_plus), + byte(DW_OP_swap), + byte(DW_OP_lit1), + byte(DW_OP_minus), + byte(DW_OP_skip), 0xf1, 0xff, + + byte(DW_OP_drop), + }) } diff --git a/pkg/dwarf/op/opcodes.go b/pkg/dwarf/op/opcodes.go index c315859e..e53088c0 100644 --- a/pkg/dwarf/op/opcodes.go +++ b/pkg/dwarf/op/opcodes.go @@ -473,9 +473,79 @@ var opcodeArgs = map[Opcode]string{ } var oplut = map[Opcode]stackfn{ DW_OP_addr: addr, + DW_OP_deref: deref, + DW_OP_const1u: constnu, + DW_OP_const1s: constns, + DW_OP_const2u: constnu, + DW_OP_const2s: constns, + DW_OP_const4u: constnu, + DW_OP_const4s: constns, + DW_OP_const8u: constnu, + DW_OP_const8s: constns, + DW_OP_constu: constu, DW_OP_consts: consts, - DW_OP_plus: plus, + DW_OP_dup: dup, + DW_OP_drop: drop, + DW_OP_over: pick, + DW_OP_pick: pick, + DW_OP_swap: swap, + DW_OP_rot: rot, + DW_OP_xderef: deref, + DW_OP_abs: unaryop, + DW_OP_and: binaryop, + DW_OP_div: binaryop, + DW_OP_minus: binaryop, + DW_OP_mod: binaryop, + DW_OP_mul: binaryop, + DW_OP_neg: unaryop, + DW_OP_not: unaryop, + DW_OP_or: binaryop, + DW_OP_plus: binaryop, DW_OP_plus_uconst: plusuconsts, + DW_OP_shl: binaryop, + DW_OP_shr: binaryop, + DW_OP_shra: binaryop, + DW_OP_xor: binaryop, + DW_OP_bra: bra, + DW_OP_eq: binaryop, + DW_OP_ge: binaryop, + DW_OP_gt: binaryop, + DW_OP_le: binaryop, + DW_OP_lt: binaryop, + DW_OP_ne: binaryop, + DW_OP_skip: skip, + DW_OP_lit0: literal, + DW_OP_lit1: literal, + DW_OP_lit2: literal, + DW_OP_lit3: literal, + DW_OP_lit4: literal, + DW_OP_lit5: literal, + DW_OP_lit6: literal, + DW_OP_lit7: literal, + DW_OP_lit8: literal, + DW_OP_lit9: literal, + DW_OP_lit10: literal, + DW_OP_lit11: literal, + DW_OP_lit12: literal, + DW_OP_lit13: literal, + DW_OP_lit14: literal, + DW_OP_lit15: literal, + DW_OP_lit16: literal, + DW_OP_lit17: literal, + DW_OP_lit18: literal, + DW_OP_lit19: literal, + DW_OP_lit20: literal, + DW_OP_lit21: literal, + DW_OP_lit22: literal, + DW_OP_lit23: literal, + DW_OP_lit24: literal, + DW_OP_lit25: literal, + DW_OP_lit26: literal, + DW_OP_lit27: literal, + DW_OP_lit28: literal, + DW_OP_lit29: literal, + DW_OP_lit30: literal, + DW_OP_lit31: literal, DW_OP_reg0: register, DW_OP_reg1: register, DW_OP_reg2: register, @@ -508,8 +578,45 @@ var oplut = map[Opcode]stackfn{ DW_OP_reg29: register, DW_OP_reg30: register, DW_OP_reg31: register, + DW_OP_breg0: bregister, + DW_OP_breg1: bregister, + DW_OP_breg2: bregister, + DW_OP_breg3: bregister, + DW_OP_breg4: bregister, + DW_OP_breg5: bregister, + DW_OP_breg6: bregister, + DW_OP_breg7: bregister, + DW_OP_breg8: bregister, + DW_OP_breg9: bregister, + DW_OP_breg10: bregister, + DW_OP_breg11: bregister, + DW_OP_breg12: bregister, + DW_OP_breg13: bregister, + DW_OP_breg14: bregister, + DW_OP_breg15: bregister, + DW_OP_breg16: bregister, + DW_OP_breg17: bregister, + DW_OP_breg18: bregister, + DW_OP_breg19: bregister, + DW_OP_breg20: bregister, + DW_OP_breg21: bregister, + DW_OP_breg22: bregister, + DW_OP_breg23: bregister, + DW_OP_breg24: bregister, + DW_OP_breg25: bregister, + DW_OP_breg26: bregister, + DW_OP_breg27: bregister, + DW_OP_breg28: bregister, + DW_OP_breg29: bregister, + DW_OP_breg30: bregister, + DW_OP_breg31: bregister, DW_OP_regx: register, DW_OP_fbreg: framebase, + DW_OP_bregx: bregister, DW_OP_piece: piece, + DW_OP_deref_size: deref, + DW_OP_xderef_size: deref, DW_OP_call_frame_cfa: callframecfa, + DW_OP_implicit_value: implicitvalue, + DW_OP_stack_value: stackvalue, } diff --git a/pkg/dwarf/op/opcodes.table b/pkg/dwarf/op/opcodes.table index f1dadc07..e49e2c0a 100644 --- a/pkg/dwarf/op/opcodes.table +++ b/pkg/dwarf/op/opcodes.table @@ -20,79 +20,79 @@ DW_OP_addr 0x03 "8" addr -DW_OP_deref 0x06 "" -DW_OP_const1u 0x08 "1" -DW_OP_const1s 0x09 "1" -DW_OP_const2u 0x0a "2" -DW_OP_const2s 0x0b "2" -DW_OP_const4u 0x0c "4" -DW_OP_const4s 0x0d "4" -DW_OP_const8u 0x0e "8" -DW_OP_const8s 0x0f "8" -DW_OP_constu 0x10 "u" +DW_OP_deref 0x06 "" deref +DW_OP_const1u 0x08 "1" constnu +DW_OP_const1s 0x09 "1" constns +DW_OP_const2u 0x0a "2" constnu +DW_OP_const2s 0x0b "2" constns +DW_OP_const4u 0x0c "4" constnu +DW_OP_const4s 0x0d "4" constns +DW_OP_const8u 0x0e "8" constnu +DW_OP_const8s 0x0f "8" constns +DW_OP_constu 0x10 "u" constu DW_OP_consts 0x11 "s" consts -DW_OP_dup 0x12 "" -DW_OP_drop 0x13 "" -DW_OP_over 0x14 "" -DW_OP_pick 0x15 "" -DW_OP_swap 0x16 "" -DW_OP_rot 0x17 "" -DW_OP_xderef 0x18 "" -DW_OP_abs 0x19 "" -DW_OP_and 0x1a "" -DW_OP_div 0x1b "" -DW_OP_minus 0x1c "" -DW_OP_mod 0x1d "" -DW_OP_mul 0x1e "" -DW_OP_neg 0x1f "" -DW_OP_not 0x20 "" -DW_OP_or 0x21 "" -DW_OP_plus 0x22 "" plus +DW_OP_dup 0x12 "" dup +DW_OP_drop 0x13 "" drop +DW_OP_over 0x14 "" pick +DW_OP_pick 0x15 "" pick +DW_OP_swap 0x16 "" swap +DW_OP_rot 0x17 "" rot +DW_OP_xderef 0x18 "" deref +DW_OP_abs 0x19 "" unaryop +DW_OP_and 0x1a "" binaryop +DW_OP_div 0x1b "" binaryop +DW_OP_minus 0x1c "" binaryop +DW_OP_mod 0x1d "" binaryop +DW_OP_mul 0x1e "" binaryop +DW_OP_neg 0x1f "" unaryop +DW_OP_not 0x20 "" unaryop +DW_OP_or 0x21 "" binaryop +DW_OP_plus 0x22 "" binaryop DW_OP_plus_uconst 0x23 "u" plusuconsts -DW_OP_shl 0x24 "" -DW_OP_shr 0x25 "" -DW_OP_shra 0x26 "" -DW_OP_xor 0x27 "" -DW_OP_bra 0x28 "2" -DW_OP_eq 0x29 "" -DW_OP_ge 0x2a "" -DW_OP_gt 0x2b "" -DW_OP_le 0x2c "" -DW_OP_lt 0x2d "" -DW_OP_ne 0x2e "" -DW_OP_skip 0x2f "2" -DW_OP_lit0 0x30 "" -DW_OP_lit1 0x31 "" -DW_OP_lit2 0x32 "" -DW_OP_lit3 0x33 "" -DW_OP_lit4 0x34 "" -DW_OP_lit5 0x35 "" -DW_OP_lit6 0x36 "" -DW_OP_lit7 0x37 "" -DW_OP_lit8 0x38 "" -DW_OP_lit9 0x39 "" -DW_OP_lit10 0x3a "" -DW_OP_lit11 0x3b "" -DW_OP_lit12 0x3c "" -DW_OP_lit13 0x3d "" -DW_OP_lit14 0x3e "" -DW_OP_lit15 0x3f "" -DW_OP_lit16 0x40 "" -DW_OP_lit17 0x41 "" -DW_OP_lit18 0x42 "" -DW_OP_lit19 0x43 "" -DW_OP_lit20 0x44 "" -DW_OP_lit21 0x45 "" -DW_OP_lit22 0x46 "" -DW_OP_lit23 0x47 "" -DW_OP_lit24 0x48 "" -DW_OP_lit25 0x49 "" -DW_OP_lit26 0x4a "" -DW_OP_lit27 0x4b "" -DW_OP_lit28 0x4c "" -DW_OP_lit29 0x4d "" -DW_OP_lit30 0x4e "" -DW_OP_lit31 0x4f "" +DW_OP_shl 0x24 "" binaryop +DW_OP_shr 0x25 "" binaryop +DW_OP_shra 0x26 "" binaryop +DW_OP_xor 0x27 "" binaryop +DW_OP_bra 0x28 "2" bra +DW_OP_eq 0x29 "" binaryop +DW_OP_ge 0x2a "" binaryop +DW_OP_gt 0x2b "" binaryop +DW_OP_le 0x2c "" binaryop +DW_OP_lt 0x2d "" binaryop +DW_OP_ne 0x2e "" binaryop +DW_OP_skip 0x2f "2" skip +DW_OP_lit0 0x30 "" literal +DW_OP_lit1 0x31 "" literal +DW_OP_lit2 0x32 "" literal +DW_OP_lit3 0x33 "" literal +DW_OP_lit4 0x34 "" literal +DW_OP_lit5 0x35 "" literal +DW_OP_lit6 0x36 "" literal +DW_OP_lit7 0x37 "" literal +DW_OP_lit8 0x38 "" literal +DW_OP_lit9 0x39 "" literal +DW_OP_lit10 0x3a "" literal +DW_OP_lit11 0x3b "" literal +DW_OP_lit12 0x3c "" literal +DW_OP_lit13 0x3d "" literal +DW_OP_lit14 0x3e "" literal +DW_OP_lit15 0x3f "" literal +DW_OP_lit16 0x40 "" literal +DW_OP_lit17 0x41 "" literal +DW_OP_lit18 0x42 "" literal +DW_OP_lit19 0x43 "" literal +DW_OP_lit20 0x44 "" literal +DW_OP_lit21 0x45 "" literal +DW_OP_lit22 0x46 "" literal +DW_OP_lit23 0x47 "" literal +DW_OP_lit24 0x48 "" literal +DW_OP_lit25 0x49 "" literal +DW_OP_lit26 0x4a "" literal +DW_OP_lit27 0x4b "" literal +DW_OP_lit28 0x4c "" literal +DW_OP_lit29 0x4d "" literal +DW_OP_lit30 0x4e "" literal +DW_OP_lit31 0x4f "" literal DW_OP_reg0 0x50 "" register DW_OP_reg1 0x51 "" register DW_OP_reg2 0x52 "" register @@ -125,44 +125,44 @@ DW_OP_reg28 0x6c "" register DW_OP_reg29 0x6d "" register DW_OP_reg30 0x6e "" register DW_OP_reg31 0x6f "" register -DW_OP_breg0 0x70 "s" -DW_OP_breg1 0x71 "s" -DW_OP_breg2 0x72 "s" -DW_OP_breg3 0x73 "s" -DW_OP_breg4 0x74 "s" -DW_OP_breg5 0x75 "s" -DW_OP_breg6 0x76 "s" -DW_OP_breg7 0x77 "s" -DW_OP_breg8 0x78 "s" -DW_OP_breg9 0x79 "s" -DW_OP_breg10 0x7a "s" -DW_OP_breg11 0x7b "s" -DW_OP_breg12 0x7c "s" -DW_OP_breg13 0x7d "s" -DW_OP_breg14 0x7e "s" -DW_OP_breg15 0x7f "s" -DW_OP_breg16 0x80 "s" -DW_OP_breg17 0x81 "s" -DW_OP_breg18 0x82 "s" -DW_OP_breg19 0x83 "s" -DW_OP_breg20 0x84 "s" -DW_OP_breg21 0x85 "s" -DW_OP_breg22 0x86 "s" -DW_OP_breg23 0x87 "s" -DW_OP_breg24 0x88 "s" -DW_OP_breg25 0x89 "s" -DW_OP_breg26 0x8a "s" -DW_OP_breg27 0x8b "s" -DW_OP_breg28 0x8c "s" -DW_OP_breg29 0x8d "s" -DW_OP_breg30 0x8e "s" -DW_OP_breg31 0x8f "s" +DW_OP_breg0 0x70 "s" bregister +DW_OP_breg1 0x71 "s" bregister +DW_OP_breg2 0x72 "s" bregister +DW_OP_breg3 0x73 "s" bregister +DW_OP_breg4 0x74 "s" bregister +DW_OP_breg5 0x75 "s" bregister +DW_OP_breg6 0x76 "s" bregister +DW_OP_breg7 0x77 "s" bregister +DW_OP_breg8 0x78 "s" bregister +DW_OP_breg9 0x79 "s" bregister +DW_OP_breg10 0x7a "s" bregister +DW_OP_breg11 0x7b "s" bregister +DW_OP_breg12 0x7c "s" bregister +DW_OP_breg13 0x7d "s" bregister +DW_OP_breg14 0x7e "s" bregister +DW_OP_breg15 0x7f "s" bregister +DW_OP_breg16 0x80 "s" bregister +DW_OP_breg17 0x81 "s" bregister +DW_OP_breg18 0x82 "s" bregister +DW_OP_breg19 0x83 "s" bregister +DW_OP_breg20 0x84 "s" bregister +DW_OP_breg21 0x85 "s" bregister +DW_OP_breg22 0x86 "s" bregister +DW_OP_breg23 0x87 "s" bregister +DW_OP_breg24 0x88 "s" bregister +DW_OP_breg25 0x89 "s" bregister +DW_OP_breg26 0x8a "s" bregister +DW_OP_breg27 0x8b "s" bregister +DW_OP_breg28 0x8c "s" bregister +DW_OP_breg29 0x8d "s" bregister +DW_OP_breg30 0x8e "s" bregister +DW_OP_breg31 0x8f "s" bregister DW_OP_regx 0x90 "s" register DW_OP_fbreg 0x91 "s" framebase -DW_OP_bregx 0x92 "us" +DW_OP_bregx 0x92 "us" bregister DW_OP_piece 0x93 "u" piece -DW_OP_deref_size 0x94 "1" -DW_OP_xderef_size 0x95 "1" +DW_OP_deref_size 0x94 "1" deref +DW_OP_xderef_size 0x95 "1" deref DW_OP_nop 0x96 "" DW_OP_push_object_address 0x97 "" DW_OP_call2 0x98 "2" @@ -171,5 +171,5 @@ DW_OP_call_ref 0x9a "4" DW_OP_form_tls_address 0x9b "" DW_OP_call_frame_cfa 0x9c "" callframecfa DW_OP_bit_piece 0x9d "uu" -DW_OP_implicit_value 0x9e "B" -DW_OP_stack_value 0x9f "" +DW_OP_implicit_value 0x9e "B" implicitvalue +DW_OP_stack_value 0x9f "" stackvalue diff --git a/pkg/dwarf/reader/reader.go b/pkg/dwarf/reader/reader.go index 8fa99571..4094833d 100644 --- a/pkg/dwarf/reader/reader.go +++ b/pkg/dwarf/reader/reader.go @@ -43,7 +43,7 @@ func (reader *Reader) AddrFor(name string, staticBase uint64, ptrSize int) (uint if !ok { return 0, fmt.Errorf("type assertion failed") } - addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{StaticBase: staticBase}, instructions, ptrSize) + addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{StaticBase: staticBase}, instructions, ptrSize, nil) if err != nil { return 0, err } @@ -69,7 +69,7 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte, p if !ok { continue } - addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...), ptrSize) + addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...), ptrSize, nil) return uint64(addr), err } } diff --git a/pkg/dwarf/util/util.go b/pkg/dwarf/util/util.go index 6ca75e1c..a0720af9 100644 --- a/pkg/dwarf/util/util.go +++ b/pkg/dwarf/util/util.go @@ -140,6 +140,12 @@ func ParseString(data *bytes.Buffer) (string, error) { // ReadUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader. func ReadUintRaw(reader io.Reader, order binary.ByteOrder, ptrSize int) (uint64, error) { switch ptrSize { + case 2: + var n uint16 + if err := binary.Read(reader, order, &n); err != nil { + return 0, err + } + return uint64(n), nil case 4: var n uint32 if err := binary.Read(reader, order, &n); err != nil { @@ -153,7 +159,7 @@ func ReadUintRaw(reader io.Reader, order binary.ByteOrder, ptrSize int) (uint64, } return n, nil } - return 0, fmt.Errorf("not supprted ptr size %d", ptrSize) + return 0, fmt.Errorf("pointer size %d not supported", ptrSize) } // WriteUint writes an integer of ptrSize bytes to writer, in the specified byte order. @@ -164,7 +170,7 @@ func WriteUint(writer io.Writer, order binary.ByteOrder, ptrSize int, data uint6 case 8: return binary.Write(writer, order, data) } - return fmt.Errorf("not support prt size %d", ptrSize) + return fmt.Errorf("pointer size %d not supported", ptrSize) } // ReadDwarfLength reads a DWARF length field followed by a version field diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index f4f57383..8eff6e09 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -936,12 +936,16 @@ func (bi *BinaryInfo) LocationCovers(entry *dwarf.Entry, attr dwarf.Attr) ([][2] // This will either be an int64 address or a slice of Pieces for locations // that don't correspond to a single memory address (registers, composite // locations). -func (bi *BinaryInfo) Location(entry godwarf.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, *locationExpr, error) { +func (bi *BinaryInfo) Location(entry godwarf.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters, mem MemoryReadWriter) (int64, []op.Piece, *locationExpr, error) { instr, descr, err := bi.locationExpr(entry, attr, pc) if err != nil { return 0, nil, nil, err } - addr, pieces, err := op.ExecuteStackProgram(regs, instr, bi.Arch.PtrSize()) + readMemory := op.ReadMemoryFunc(nil) + if mem != nil { + readMemory = mem.ReadMemory + } + addr, pieces, err := op.ExecuteStackProgram(regs, instr, bi.Arch.PtrSize(), readMemory) return addr, pieces, descr, err } diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go index b17a0694..b5294385 100644 --- a/pkg/proc/fncall.go +++ b/pkg/proc/fncall.go @@ -661,7 +661,7 @@ func funcCallArgOldABI(fn *Function, bi *BinaryInfo, entry reader.Variable, argn err = fmt.Errorf("could not get argument location of %s: %v", argname, err) } else { var pieces []op.Piece - off, pieces, err = op.ExecuteStackProgram(op.DwarfRegisters{CFA: CFA, FrameBase: CFA}, locprog, bi.Arch.PtrSize()) + off, pieces, err = op.ExecuteStackProgram(op.DwarfRegisters{CFA: CFA, FrameBase: CFA}, locprog, bi.Arch.PtrSize(), nil) if err != nil { err = fmt.Errorf("unsupported location expression for argument %s: %v", argname, err) } @@ -962,7 +962,7 @@ func fakeFunctionEntryScope(scope *EvalScope, fn *Function, cfa int64, sp uint64 if err != nil { return err } - scope.Regs.FrameBase, _, _, _ = scope.BinInfo.Location(e, dwarf.AttrFrameBase, scope.PC, scope.Regs) + scope.Regs.FrameBase, _, _, _ = scope.BinInfo.Location(e, dwarf.AttrFrameBase, scope.PC, scope.Regs, nil) return nil } diff --git a/pkg/proc/mem.go b/pkg/proc/mem.go index e042bdc8..281a0158 100644 --- a/pkg/proc/mem.go +++ b/pkg/proc/mem.go @@ -115,7 +115,7 @@ func newCompositeMemory(mem MemoryReadWriter, arch *Arch, regs op.DwarfRegisters switch piece.Kind { case op.RegPiece: reg := regs.Bytes(piece.Val) - if piece.Size == 0 && len(pieces) == 1 { + if piece.Size == 0 && i == len(pieces)-1 { piece.Size = len(reg) } if piece.Size > len(reg) { @@ -130,12 +130,18 @@ func newCompositeMemory(mem MemoryReadWriter, arch *Arch, regs op.DwarfRegisters mem.ReadMemory(buf, uint64(piece.Val)) cmem.data = append(cmem.data, buf...) case op.ImmPiece: - sz := 8 - if piece.Size > sz { - sz = piece.Size + buf := piece.Bytes + if buf == nil { + sz := 8 + if piece.Size > sz { + sz = piece.Size + } + if piece.Size == 0 && i == len(pieces)-1 { + piece.Size = arch.PtrSize() // DWARF doesn't say what this should be + } + buf = make([]byte, sz) + binary.LittleEndian.PutUint64(buf, piece.Val) } - buf := make([]byte, sz) - binary.LittleEndian.PutUint64(buf, piece.Val) cmem.data = append(cmem.data, buf[:piece.Size]...) default: panic("unsupported piece kind") diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index 9448dd2f..0d8e2b39 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -259,7 +259,7 @@ func (it *stackIterator) frameBase(fn *Function) int64 { if err != nil { return 0 } - fb, _, _, _ := it.bi.Location(dwarfTree.Entry, dwarf.AttrFrameBase, it.pc, it.regs) + fb, _, _, _ := it.bi.Location(dwarfTree.Entry, dwarf.AttrFrameBase, it.pc, it.regs, it.mem) return fb } @@ -459,13 +459,13 @@ func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, c case frame.RuleRegister: return it.regs.Reg(rule.Reg), nil case frame.RuleExpression: - v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression, it.bi.Arch.PtrSize()) + v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression, it.bi.Arch.PtrSize(), it.mem.ReadMemory) if err != nil { return nil, err } return it.readRegisterAt(regnum, uint64(v)) case frame.RuleValExpression: - v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression, it.bi.Arch.PtrSize()) + v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression, it.bi.Arch.PtrSize(), it.mem.ReadMemory) if err != nil { return nil, err } @@ -671,7 +671,7 @@ func (d *Defer) EvalScope(t *Target, thread Thread) (*EvalScope, error) { if err != nil { return nil, fmt.Errorf("could not read DWARF function entry: %v", err) } - scope.Regs.FrameBase, _, _, _ = bi.Location(e, dwarf.AttrFrameBase, scope.PC, scope.Regs) + scope.Regs.FrameBase, _, _, _ = bi.Location(e, dwarf.AttrFrameBase, scope.PC, scope.Regs, scope.Mem) scope.Mem = cacheMemory(scope.Mem, uint64(scope.Regs.CFA), int(d.argSz)) return scope, nil diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 8fa5242c..974828ee 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -1140,7 +1140,7 @@ func extractVarInfoFromEntry(tgt *Target, bi *BinaryInfo, image *Image, regs op. return nil, err } - addr, pieces, descr, err := bi.Location(entry, dwarf.AttrLocation, regs.PC(), regs) + addr, pieces, descr, err := bi.Location(entry, dwarf.AttrLocation, regs.PC(), regs, mem) if pieces != nil { var cmem *compositeMemory if tgt != nil {