gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/proc/mem.go (about)

     1  package proc
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"gitlab.com/Raven-IO/raven-delve/pkg/dwarf/op"
     9  )
    10  
    11  const cacheEnabled = true
    12  
    13  // MemoryReader is like io.ReaderAt, but the offset is a uint64 so that it
    14  // can address all of 64-bit memory.
    15  // Redundant with memoryReadWriter but more easily suited to working with
    16  // the standard io package.
    17  type MemoryReader interface {
    18  	// ReadMemory is just like io.ReaderAt.ReadAt.
    19  	ReadMemory(buf []byte, addr uint64) (n int, err error)
    20  }
    21  
    22  // MemoryReadWriter is an interface for reading or writing to
    23  // the targets memory. This allows us to read from the actual
    24  // target memory or possibly a cache.
    25  type MemoryReadWriter interface {
    26  	MemoryReader
    27  	WriteMemory(addr uint64, data []byte) (written int, err error)
    28  }
    29  
    30  type memCache struct {
    31  	loaded    bool
    32  	cacheAddr uint64
    33  	cache     []byte
    34  	mem       MemoryReadWriter
    35  }
    36  
    37  func (m *memCache) contains(addr uint64, size int) bool {
    38  	return addr >= m.cacheAddr && addr <= (m.cacheAddr+uint64(len(m.cache)-size))
    39  }
    40  
    41  func (m *memCache) ReadMemory(data []byte, addr uint64) (n int, err error) {
    42  	if m.contains(addr, len(data)) {
    43  		if !m.loaded {
    44  			_, err := m.mem.ReadMemory(m.cache, m.cacheAddr)
    45  			if err != nil {
    46  				return 0, err
    47  			}
    48  			m.loaded = true
    49  		}
    50  		copy(data, m.cache[addr-m.cacheAddr:])
    51  		return len(data), nil
    52  	}
    53  
    54  	return m.mem.ReadMemory(data, addr)
    55  }
    56  
    57  func (m *memCache) WriteMemory(addr uint64, data []byte) (written int, err error) {
    58  	return m.mem.WriteMemory(addr, data)
    59  }
    60  
    61  func CreateLoadedCachedMemory(data []byte) MemoryReadWriter {
    62  	return &memCache{loaded: true, cacheAddr: fakeAddressUnresolv, cache: data, mem: nil}
    63  }
    64  
    65  func cacheMemory(mem MemoryReadWriter, addr uint64, size int) MemoryReadWriter {
    66  	if !cacheEnabled {
    67  		return mem
    68  	}
    69  	if size <= 0 {
    70  		return mem
    71  	}
    72  	switch cacheMem := mem.(type) {
    73  	case *memCache:
    74  		if cacheMem.contains(addr, size) {
    75  			return mem
    76  		}
    77  	case *compositeMemory:
    78  		return mem
    79  	}
    80  	return &memCache{false, addr, make([]byte, size), mem}
    81  }
    82  
    83  // compositeMemory represents a chunk of memory that is stored in CPU
    84  // registers or non-contiguously.
    85  //
    86  // When optimizations are enabled the compiler will store some variables
    87  // into registers and sometimes it will also store structs non-contiguously
    88  // with some fields stored into CPU registers and other fields stored in
    89  // memory.
    90  type compositeMemory struct {
    91  	base    uint64 // base address for this composite memory
    92  	realmem MemoryReadWriter
    93  	arch    *Arch
    94  	regs    op.DwarfRegisters
    95  	pieces  []op.Piece
    96  	data    []byte
    97  }
    98  
    99  // CreateCompositeMemory created a new composite memory type using the provided MemoryReadWriter as the
   100  // underlying memory buffer.
   101  func CreateCompositeMemory(mem MemoryReadWriter, arch *Arch, regs op.DwarfRegisters, pieces []op.Piece, size int64) (*compositeMemory, error) {
   102  	// This is basically a small wrapper to avoid having to change all callers
   103  	// of newCompositeMemory since it existed first.
   104  	cm, err := newCompositeMemory(mem, arch, regs, pieces, size)
   105  	if cm != nil {
   106  		cm.base = fakeAddressUnresolv
   107  	}
   108  	return cm, err
   109  }
   110  
   111  func newCompositeMemory(mem MemoryReadWriter, arch *Arch, regs op.DwarfRegisters, pieces []op.Piece, size int64) (*compositeMemory, error) {
   112  	cmem := &compositeMemory{realmem: mem, arch: arch, regs: regs, pieces: pieces, data: []byte{}}
   113  	for i := range pieces {
   114  		piece := &pieces[i]
   115  		switch piece.Kind {
   116  		case op.RegPiece:
   117  			reg := regs.Bytes(piece.Val)
   118  			if piece.Size == 0 && i == len(pieces)-1 {
   119  				piece.Size = len(reg)
   120  			}
   121  			if piece.Size > len(reg) {
   122  				if regs.FloatLoadError != nil {
   123  					return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d), also error loading floating point registers: %v", piece.Size, piece.Val, len(reg), regs.FloatLoadError)
   124  				}
   125  				return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d)", piece.Size, piece.Val, len(reg))
   126  			}
   127  			cmem.data = append(cmem.data, reg[:piece.Size]...)
   128  		case op.AddrPiece:
   129  			buf := make([]byte, piece.Size)
   130  			mem.ReadMemory(buf, piece.Val)
   131  			cmem.data = append(cmem.data, buf...)
   132  		case op.ImmPiece:
   133  			buf := piece.Bytes
   134  			if buf == nil {
   135  				sz := 8
   136  				if piece.Size > sz {
   137  					sz = piece.Size
   138  				}
   139  				if piece.Size == 0 && i == len(pieces)-1 {
   140  					piece.Size = arch.PtrSize() // DWARF doesn't say what this should be
   141  				}
   142  				buf = make([]byte, sz)
   143  				binary.LittleEndian.PutUint64(buf, piece.Val)
   144  			}
   145  			cmem.data = append(cmem.data, buf[:piece.Size]...)
   146  		default:
   147  			panic("unsupported piece kind")
   148  		}
   149  	}
   150  	paddingBytes := int(size) - len(cmem.data)
   151  	if paddingBytes > 0 && paddingBytes < arch.ptrSize {
   152  		padding := make([]byte, paddingBytes)
   153  		cmem.data = append(cmem.data, padding...)
   154  	}
   155  	return cmem, nil
   156  }
   157  
   158  func (mem *compositeMemory) ReadMemory(data []byte, addr uint64) (int, error) {
   159  	addr -= mem.base
   160  	if addr >= uint64(len(mem.data)) || addr+uint64(len(data)) > uint64(len(mem.data)) {
   161  		return 0, errors.New("read out of bounds")
   162  	}
   163  	copy(data, mem.data[addr:addr+uint64(len(data))])
   164  	return len(data), nil
   165  }
   166  
   167  func (mem *compositeMemory) WriteMemory(addr uint64, data []byte) (int, error) {
   168  	addr -= mem.base
   169  	if addr >= uint64(len(mem.data)) || addr+uint64(len(data)) > uint64(len(mem.data)) {
   170  		return 0, errors.New("write out of bounds")
   171  	}
   172  	if mem.regs.ChangeFunc == nil {
   173  		return 0, errors.New("can not write registers")
   174  	}
   175  
   176  	copy(mem.data[addr:], data)
   177  
   178  	curAddr := uint64(0)
   179  	donesz := 0
   180  	for _, piece := range mem.pieces {
   181  		if curAddr < (addr+uint64(len(data))) && addr < (curAddr+uint64(piece.Size)) {
   182  			// changed memory interval overlaps current piece
   183  			pieceMem := mem.data[curAddr : curAddr+uint64(piece.Size)]
   184  
   185  			switch piece.Kind {
   186  			case op.RegPiece:
   187  				oldReg := mem.regs.Reg(piece.Val)
   188  				newReg := op.DwarfRegisterFromBytes(pieceMem)
   189  				err := mem.regs.ChangeFunc(piece.Val, oldReg.Overwrite(newReg))
   190  				if err != nil {
   191  					return donesz, err
   192  				}
   193  			case op.AddrPiece:
   194  				n, err := mem.realmem.WriteMemory(piece.Val, pieceMem)
   195  				if err != nil {
   196  					return donesz + n, err
   197  				}
   198  			case op.ImmPiece:
   199  				//TODO(aarzilli): maybe return an error if the user tried to change the value?
   200  				// nothing to do
   201  			default:
   202  				panic("unsupported piece kind")
   203  			}
   204  			donesz += piece.Size
   205  		}
   206  		curAddr += uint64(piece.Size)
   207  	}
   208  
   209  	return len(data), nil
   210  }
   211  
   212  // DereferenceMemory returns a MemoryReadWriter that can read and write the
   213  // memory pointed to by pointers in this memory.
   214  // Normally mem and mem.Dereference are the same object, they are different
   215  // only if this MemoryReadWriter is used to access memory outside of the
   216  // normal address space of the inferior process (such as data contained in
   217  // registers, or composite memory).
   218  func DereferenceMemory(mem MemoryReadWriter) MemoryReadWriter {
   219  	switch mem := mem.(type) {
   220  	case *compositeMemory:
   221  		return mem.realmem
   222  	}
   223  	return mem
   224  }