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 }