2016-01-10 12:49:03 +00:00
package proc
2017-08-24 07:46:47 +00:00
import (
"errors"
2018-11-19 13:22:43 +00:00
"fmt"
2017-08-24 07:46:47 +00:00
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/dwarf/op"
2017-08-24 07:46:47 +00:00
)
2016-01-10 12:49:03 +00:00
const cacheEnabled = true
2020-09-09 17:36:15 +00:00
// MemoryReader is like io.ReaderAt, but the offset is a uint64 so that it
2017-04-18 14:24:45 +00:00
// 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.
2020-09-09 17:36:15 +00:00
ReadMemory ( buf [ ] byte , addr uint64 ) ( n int , err error )
2017-04-18 14:24:45 +00:00
}
2018-08-31 18:08:18 +00:00
// 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.
2017-04-21 06:55:53 +00:00
type MemoryReadWriter interface {
2017-04-18 14:24:45 +00:00
MemoryReader
2020-09-09 17:36:15 +00:00
WriteMemory ( addr uint64 , data [ ] byte ) ( written int , err error )
2016-01-10 12:49:03 +00:00
}
type memCache struct {
2018-04-14 12:57:35 +00:00
loaded bool
2020-09-09 17:36:15 +00:00
cacheAddr uint64
2016-01-10 12:49:03 +00:00
cache [ ] byte
2017-04-21 06:55:53 +00:00
mem MemoryReadWriter
2016-01-10 12:49:03 +00:00
}
2020-09-09 17:36:15 +00:00
func ( m * memCache ) contains ( addr uint64 , size int ) bool {
return addr >= m . cacheAddr && addr <= ( m . cacheAddr + uint64 ( len ( m . cache ) - size ) )
2016-01-10 12:49:03 +00:00
}
2020-09-09 17:36:15 +00:00
func ( m * memCache ) ReadMemory ( data [ ] byte , addr uint64 ) ( n int , err error ) {
2017-04-18 14:24:45 +00:00
if m . contains ( addr , len ( data ) ) {
2018-04-14 12:57:35 +00:00
if ! m . loaded {
_ , err := m . mem . ReadMemory ( m . cache , m . cacheAddr )
if err != nil {
return 0 , err
}
m . loaded = true
}
2017-04-18 14:24:45 +00:00
copy ( data , m . cache [ addr - m . cacheAddr : ] )
return len ( data ) , nil
2016-01-10 12:49:03 +00:00
}
2017-04-18 14:24:45 +00:00
return m . mem . ReadMemory ( data , addr )
2016-01-10 12:49:03 +00:00
}
2020-09-09 17:36:15 +00:00
func ( m * memCache ) WriteMemory ( addr uint64 , data [ ] byte ) ( written int , err error ) {
2017-04-21 06:55:53 +00:00
return m . mem . WriteMemory ( addr , data )
2016-01-10 12:49:03 +00:00
}
2020-09-09 17:36:15 +00:00
func cacheMemory ( mem MemoryReadWriter , addr uint64 , size int ) MemoryReadWriter {
2016-01-10 12:49:03 +00:00
if ! cacheEnabled {
return mem
}
2016-01-27 12:21:26 +00:00
if size <= 0 {
return mem
}
2017-08-24 07:46:47 +00:00
switch cacheMem := mem . ( type ) {
case * memCache :
2016-01-10 12:49:03 +00:00
if cacheMem . contains ( addr , size ) {
return mem
}
2017-08-24 07:46:47 +00:00
case * compositeMemory :
return mem
2016-01-10 12:49:03 +00:00
}
2018-04-14 12:57:35 +00:00
return & memCache { false , addr , make ( [ ] byte , size ) , mem }
2016-01-10 12:49:03 +00:00
}
2017-08-24 07:46:47 +00:00
// fakeAddress used by extractVarInfoFromEntry for variables that do not
// have a memory address, we can't use 0 because a lot of code (likely
// including client code) assumes that addr == 0 is nil
const fakeAddress = 0xbeef0000
2018-01-27 15:50:18 +00:00
// 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.
2017-08-24 07:46:47 +00:00
type compositeMemory struct {
realmem MemoryReadWriter
regs op . DwarfRegisters
pieces [ ] op . Piece
data [ ] byte
}
2018-11-19 13:22:43 +00:00
func newCompositeMemory ( mem MemoryReadWriter , regs op . DwarfRegisters , pieces [ ] op . Piece ) ( * compositeMemory , error ) {
2017-08-24 07:46:47 +00:00
cmem := & compositeMemory { realmem : mem , regs : regs , pieces : pieces , data : [ ] byte { } }
for _ , piece := range pieces {
if piece . IsRegister {
reg := regs . Bytes ( piece . RegNum )
sz := piece . Size
if sz == 0 && len ( pieces ) == 1 {
sz = len ( reg )
}
2018-11-19 13:22:43 +00:00
if sz > len ( reg ) {
2020-05-13 18:56:50 +00:00
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" , sz , piece . RegNum , len ( reg ) , regs . FloatLoadError )
}
2018-11-19 13:22:43 +00:00
return nil , fmt . Errorf ( "could not read %d bytes from register %d (size: %d)" , sz , piece . RegNum , len ( reg ) )
}
2017-08-24 07:46:47 +00:00
cmem . data = append ( cmem . data , reg [ : sz ] ... )
} else {
buf := make ( [ ] byte , piece . Size )
2020-09-09 17:36:15 +00:00
mem . ReadMemory ( buf , uint64 ( piece . Addr ) )
2017-08-24 07:46:47 +00:00
cmem . data = append ( cmem . data , buf ... )
}
}
2018-11-19 13:22:43 +00:00
return cmem , nil
2017-08-24 07:46:47 +00:00
}
2020-09-09 17:36:15 +00:00
func ( mem * compositeMemory ) ReadMemory ( data [ ] byte , addr uint64 ) ( int , error ) {
2017-08-24 07:46:47 +00:00
addr -= fakeAddress
2020-09-09 17:36:15 +00:00
if addr >= uint64 ( len ( mem . data ) ) || addr + uint64 ( len ( data ) ) > uint64 ( len ( mem . data ) ) {
2017-08-24 07:46:47 +00:00
return 0 , errors . New ( "read out of bounds" )
}
2020-09-09 17:36:15 +00:00
copy ( data , mem . data [ addr : addr + uint64 ( len ( data ) ) ] )
2017-08-24 07:46:47 +00:00
return len ( data ) , nil
}
2020-09-09 17:36:15 +00:00
func ( mem * compositeMemory ) WriteMemory ( addr uint64 , data [ ] byte ) ( int , error ) {
2017-08-24 07:46:47 +00:00
//TODO(aarzilli): implement
return 0 , errors . New ( "can't write composite memory" )
}
// 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
}