github.com/undoio/delve@v1.9.0/pkg/proc/mem.go (about)

     1  package proc
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/undoio/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) (*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)
   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) (*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, uint64(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  	return cmem, nil
   151  }
   152  
   153  func (mem *compositeMemory) ReadMemory(data []byte, addr uint64) (int, error) {
   154  	addr -= mem.base
   155  	if addr >= uint64(len(mem.data)) || addr+uint64(len(data)) > uint64(len(mem.data)) {
   156  		return 0, errors.New("read out of bounds")
   157  	}
   158  	copy(data, mem.data[addr:addr+uint64(len(data))])
   159  	return len(data), nil
   160  }
   161  
   162  func (mem *compositeMemory) WriteMemory(addr uint64, data []byte) (int, error) {
   163  	addr -= mem.base
   164  	if addr >= uint64(len(mem.data)) || addr+uint64(len(data)) > uint64(len(mem.data)) {
   165  		return 0, errors.New("write out of bounds")
   166  	}
   167  	if mem.regs.ChangeFunc == nil {
   168  		return 0, errors.New("can not write registers")
   169  	}
   170  
   171  	copy(mem.data[addr:], data)
   172  
   173  	curAddr := uint64(0)
   174  	donesz := 0
   175  	for _, piece := range mem.pieces {
   176  		if curAddr < (addr+uint64(len(data))) && addr < (curAddr+uint64(piece.Size)) {
   177  			// changed memory interval overlaps current piece
   178  			pieceMem := mem.data[curAddr : curAddr+uint64(piece.Size)]
   179  
   180  			switch piece.Kind {
   181  			case op.RegPiece:
   182  				oldReg := mem.regs.Reg(piece.Val)
   183  				newReg := op.DwarfRegisterFromBytes(pieceMem)
   184  				err := mem.regs.ChangeFunc(piece.Val, oldReg.Overwrite(newReg))
   185  				if err != nil {
   186  					return donesz, err
   187  				}
   188  			case op.AddrPiece:
   189  				n, err := mem.realmem.WriteMemory(uint64(piece.Val), pieceMem)
   190  				if err != nil {
   191  					return donesz + n, err
   192  				}
   193  			case op.ImmPiece:
   194  				//TODO(aarzilli): maybe return an error if the user tried to change the value?
   195  				// nothing to do
   196  			default:
   197  				panic("unsupported piece kind")
   198  			}
   199  			donesz += piece.Size
   200  		}
   201  		curAddr += uint64(piece.Size)
   202  	}
   203  
   204  	return len(data), nil
   205  }
   206  
   207  // DereferenceMemory returns a MemoryReadWriter that can read and write the
   208  // memory pointed to by pointers in this memory.
   209  // Normally mem and mem.Dereference are the same object, they are different
   210  // only if this MemoryReadWriter is used to access memory outside of the
   211  // normal address space of the inferior process (such as data contained in
   212  // registers, or composite memory).
   213  func DereferenceMemory(mem MemoryReadWriter) MemoryReadWriter {
   214  	switch mem := mem.(type) {
   215  	case *compositeMemory:
   216  		return mem.realmem
   217  	}
   218  	return mem
   219  }