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 }