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