dwarf/op,proc: implement more DWARF expression opcodes (#2606)

This commit is contained in:
Alessandro Arzilli 2021-08-03 18:51:15 +02:00 committed by GitHub
parent 229fcf1559
commit fdb5189e8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 664 additions and 176 deletions

View File

@ -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
}

View File

@ -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),
})
}

View File

@ -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,
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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")

View File

@ -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

View File

@ -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 {