2016-01-10 12:49:03 +00:00
package proc
2017-08-24 07:46:47 +00:00
import (
2021-05-17 17:26:49 +00:00
"encoding/binary"
2017-08-24 07:46:47 +00:00
"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 {
2024-07-01 04:34:35 +00:00
end := addr + uint64 ( size )
if end < addr {
// overflow
return false
}
return addr >= m . cacheAddr && end <= m . cacheAddr + uint64 ( len ( m . cache ) )
2016-01-10 12:49:03 +00:00
}
2024-10-04 17:44:57 +00:00
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
}
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 {
2024-10-04 17:44:57 +00:00
err := m . load ( )
2018-04-14 12:57:35 +00:00
if err != nil {
return 0 , err
}
}
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
}
2021-07-31 15:16:26 +00:00
func CreateLoadedCachedMemory ( data [ ] byte ) MemoryReadWriter {
return & memCache { loaded : true , cacheAddr : fakeAddressUnresolv , cache : data , mem : nil }
}
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
}
2024-07-01 04:34:35 +00:00
if addr + uint64 ( size ) < addr {
// overflow
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
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 {
2021-07-02 16:37:55 +00:00
base uint64 // base address for this composite memory
2017-08-24 07:46:47 +00:00
realmem MemoryReadWriter
2021-03-04 18:28:28 +00:00
arch * Arch
2017-08-24 07:46:47 +00:00
regs op . DwarfRegisters
pieces [ ] op . Piece
data [ ] byte
}
2021-07-31 15:16:26 +00:00
// CreateCompositeMemory created a new composite memory type using the provided MemoryReadWriter as the
// underlying memory buffer.
2023-05-15 21:46:33 +00:00
func CreateCompositeMemory ( mem MemoryReadWriter , arch * Arch , regs op . DwarfRegisters , pieces [ ] op . Piece , size int64 ) ( * compositeMemory , error ) {
2021-07-31 15:16:26 +00:00
// This is basically a small wrapper to avoid having to change all callers
// of newCompositeMemory since it existed first.
2023-05-15 21:46:33 +00:00
cm , err := newCompositeMemory ( mem , arch , regs , pieces , size )
2021-07-31 15:16:26 +00:00
if cm != nil {
cm . base = fakeAddressUnresolv
}
return cm , err
}
2023-05-15 21:46:33 +00:00
func newCompositeMemory ( mem MemoryReadWriter , arch * Arch , regs op . DwarfRegisters , pieces [ ] op . Piece , size int64 ) ( * compositeMemory , error ) {
2021-03-04 18:28:28 +00:00
cmem := & compositeMemory { realmem : mem , arch : arch , regs : regs , pieces : pieces , data : [ ] byte { } }
for i := range pieces {
piece := & pieces [ i ]
2021-05-17 17:26:49 +00:00
switch piece . Kind {
case op . RegPiece :
reg := regs . Bytes ( piece . Val )
2021-08-03 16:51:15 +00:00
if piece . Size == 0 && i == len ( pieces ) - 1 {
2021-03-04 18:28:28 +00:00
piece . Size = len ( reg )
2017-08-24 07:46:47 +00:00
}
2021-03-04 18:28:28 +00:00
if piece . Size > len ( reg ) {
2020-05-13 18:56:50 +00:00
if regs . FloatLoadError != nil {
2021-05-17 17:26:49 +00:00
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 )
2020-05-13 18:56:50 +00:00
}
2021-05-17 17:26:49 +00:00
return nil , fmt . Errorf ( "could not read %d bytes from register %d (size: %d)" , piece . Size , piece . Val , len ( reg ) )
2018-11-19 13:22:43 +00:00
}
2021-03-04 18:28:28 +00:00
cmem . data = append ( cmem . data , reg [ : piece . Size ] ... )
2021-05-17 17:26:49 +00:00
case op . AddrPiece :
2017-08-24 07:46:47 +00:00
buf := make ( [ ] byte , piece . Size )
2023-11-14 15:36:55 +00:00
mem . ReadMemory ( buf , piece . Val )
2017-08-24 07:46:47 +00:00
cmem . data = append ( cmem . data , buf ... )
2021-05-17 17:26:49 +00:00
case op . ImmPiece :
2021-08-03 16:51:15 +00:00
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 )
2021-05-17 17:26:49 +00:00
}
cmem . data = append ( cmem . data , buf [ : piece . Size ] ... )
default :
panic ( "unsupported piece kind" )
2017-08-24 07:46:47 +00:00
}
}
2023-05-15 21:46:33 +00:00
paddingBytes := int ( size ) - len ( cmem . data )
if paddingBytes > 0 && paddingBytes < arch . ptrSize {
padding := make ( [ ] byte , paddingBytes )
cmem . data = append ( cmem . data , padding ... )
}
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 ) {
2021-07-02 16:37:55 +00:00
addr -= mem . base
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 ) {
2021-07-02 16:37:55 +00:00
addr -= mem . base
2021-03-04 18:28:28 +00:00
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 ) ]
2021-05-17 17:26:49 +00:00
switch piece . Kind {
case op . RegPiece :
2021-09-24 22:27:44 +00:00
oldReg := mem . regs . Reg ( piece . Val )
newReg := op . DwarfRegisterFromBytes ( pieceMem )
err := mem . regs . ChangeFunc ( piece . Val , oldReg . Overwrite ( newReg ) )
2021-03-04 18:28:28 +00:00
if err != nil {
return donesz , err
}
2021-05-17 17:26:49 +00:00
case op . AddrPiece :
2023-11-14 15:36:55 +00:00
n , err := mem . realmem . WriteMemory ( piece . Val , pieceMem )
2021-03-04 18:28:28 +00:00
if err != nil {
return donesz + n , err
}
2021-05-17 17:26:49 +00:00
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" )
2021-03-04 18:28:28 +00:00
}
donesz += piece . Size
}
curAddr += uint64 ( piece . Size )
}
return len ( data ) , nil
2017-08-24 07:46:47 +00:00
}
// 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
}