gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/proc/core/core.go (about)

     1  package core
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  
     8  	"gitlab.com/Raven-IO/raven-delve/pkg/dwarf/op"
     9  	"gitlab.com/Raven-IO/raven-delve/pkg/elfwriter"
    10  	"gitlab.com/Raven-IO/raven-delve/pkg/proc"
    11  	"gitlab.com/Raven-IO/raven-delve/pkg/proc/internal/ebpf"
    12  )
    13  
    14  // ErrNoThreads core file did not contain any threads.
    15  var ErrNoThreads = errors.New("no threads found in core file")
    16  
    17  // A SplicedMemory represents a memory space formed from multiple regions,
    18  // each of which may override previously regions. For example, in the following
    19  // core, the program text was loaded at 0x400000:
    20  // Start               End                 Page Offset
    21  // 0x0000000000400000  0x000000000044f000  0x0000000000000000
    22  // but then it's partially overwritten with an RW mapping whose data is stored
    23  // in the core file:
    24  //
    25  //	Type           Offset             VirtAddr           PhysAddr
    26  //	               FileSiz            MemSiz              Flags  Align
    27  //	LOAD           0x0000000000004000 0x000000000049a000 0x0000000000000000
    28  //	               0x0000000000002000 0x0000000000002000  RW     1000
    29  //
    30  // This can be represented in a SplicedMemory by adding the original region,
    31  // then putting the RW mapping on top of it.
    32  type SplicedMemory struct {
    33  	readers []readerEntry
    34  }
    35  
    36  type readerEntry struct {
    37  	offset uint64
    38  	length uint64
    39  	reader proc.MemoryReader
    40  }
    41  
    42  // Add adds a new region to the SplicedMemory, which may override existing regions.
    43  func (r *SplicedMemory) Add(reader proc.MemoryReader, off, length uint64) {
    44  	if length == 0 {
    45  		return
    46  	}
    47  	end := off + length - 1
    48  	newReaders := make([]readerEntry, 0, len(r.readers))
    49  	add := func(e readerEntry) {
    50  		if e.length == 0 {
    51  			return
    52  		}
    53  		newReaders = append(newReaders, e)
    54  	}
    55  	inserted := false
    56  	// Walk through the list of regions, fixing up any that overlap and inserting the new one.
    57  	for _, entry := range r.readers {
    58  		entryEnd := entry.offset + entry.length - 1
    59  		switch {
    60  		case entryEnd < off:
    61  			// Entry is completely before the new region.
    62  			add(entry)
    63  		case end < entry.offset:
    64  			// Entry is completely after the new region.
    65  			if !inserted {
    66  				add(readerEntry{off, length, reader})
    67  				inserted = true
    68  			}
    69  			add(entry)
    70  		case off <= entry.offset && entryEnd <= end:
    71  			// Entry is completely overwritten by the new region. Drop.
    72  		case entry.offset < off && entryEnd <= end:
    73  			// New region overwrites the end of the entry.
    74  			entry.length = off - entry.offset
    75  			add(entry)
    76  		case off <= entry.offset && end < entryEnd:
    77  			// New reader overwrites the beginning of the entry.
    78  			if !inserted {
    79  				add(readerEntry{off, length, reader})
    80  				inserted = true
    81  			}
    82  			overlap := entry.offset - off
    83  			entry.offset += overlap
    84  			entry.length -= overlap
    85  			add(entry)
    86  		case entry.offset < off && end < entryEnd:
    87  			// New region punches a hole in the entry. Split it in two and put the new region in the middle.
    88  			add(readerEntry{entry.offset, off - entry.offset, entry.reader})
    89  			add(readerEntry{off, length, reader})
    90  			add(readerEntry{end + 1, entryEnd - end, entry.reader})
    91  			inserted = true
    92  		default:
    93  			panic(fmt.Sprintf("Unhandled case: existing entry is %v len %v, new is %v len %v", entry.offset, entry.length, off, length))
    94  		}
    95  	}
    96  	if !inserted {
    97  		newReaders = append(newReaders, readerEntry{off, length, reader})
    98  	}
    99  	r.readers = newReaders
   100  }
   101  
   102  // ReadMemory implements MemoryReader.ReadMemory.
   103  func (r *SplicedMemory) ReadMemory(buf []byte, addr uint64) (n int, err error) {
   104  	started := false
   105  	for _, entry := range r.readers {
   106  		if entry.offset+entry.length <= addr {
   107  			if !started {
   108  				continue
   109  			}
   110  			return n, fmt.Errorf("hit unmapped area at %v after %v bytes", addr, n)
   111  		}
   112  
   113  		// The reading of the memory has been started after the first iteration
   114  		started = true
   115  
   116  		// Don't go past the region.
   117  		pb := buf
   118  		if addr+uint64(len(buf)) > entry.offset+entry.length {
   119  			pb = pb[:entry.offset+entry.length-addr]
   120  		}
   121  		pn, err := entry.reader.ReadMemory(pb, addr)
   122  		n += pn
   123  		if err != nil {
   124  			return n, fmt.Errorf("error while reading spliced memory at %#x: %v", addr, err)
   125  		}
   126  		if pn != len(pb) {
   127  			return n, nil
   128  		}
   129  		buf = buf[pn:]
   130  		addr += uint64(pn)
   131  		if len(buf) == 0 {
   132  			// Done, don't bother scanning the rest.
   133  			return n, nil
   134  		}
   135  	}
   136  	if n == 0 {
   137  		return 0, fmt.Errorf("offset %v did not match any regions", addr)
   138  	}
   139  	return n, nil
   140  }
   141  
   142  // offsetReaderAt wraps a ReaderAt into a MemoryReader, subtracting a fixed
   143  // offset from the address. This is useful to represent a mapping in an address
   144  // space. For example, if program text is mapped in at 0x400000, an
   145  // OffsetReaderAt with offset 0x400000 can be wrapped around file.Open(program)
   146  // to return the results of a read in that part of the address space.
   147  type offsetReaderAt struct {
   148  	reader io.ReaderAt
   149  	offset uint64
   150  }
   151  
   152  // ReadMemory will read the memory at addr-offset.
   153  func (r *offsetReaderAt) ReadMemory(buf []byte, addr uint64) (n int, err error) {
   154  	return r.reader.ReadAt(buf, int64(addr-r.offset))
   155  }
   156  
   157  // process represents a core file.
   158  type process struct {
   159  	mem     proc.MemoryReader
   160  	Threads map[int]*thread
   161  	pid     int
   162  
   163  	entryPoint uint64
   164  
   165  	bi          *proc.BinaryInfo
   166  	breakpoints proc.BreakpointMap
   167  }
   168  
   169  // thread represents a thread in the core file being debugged.
   170  type thread struct {
   171  	th     osThread
   172  	p      *process
   173  	common proc.CommonThread
   174  }
   175  
   176  type osThread interface {
   177  	registers() (proc.Registers, error)
   178  	pid() int
   179  }
   180  
   181  var (
   182  	// ErrWriteCore is returned when attempting to write to the core
   183  	// process memory.
   184  	ErrWriteCore = errors.New("can not write to core process")
   185  
   186  	// ErrShortRead is returned on a short read.
   187  	ErrShortRead = errors.New("short read")
   188  
   189  	// ErrContinueCore is returned when trying to continue execution of a core process.
   190  	ErrContinueCore = errors.New("can not continue execution of core process")
   191  
   192  	// ErrChangeRegisterCore is returned when trying to change register values for core files.
   193  	ErrChangeRegisterCore = errors.New("can not change register values of core process")
   194  )
   195  
   196  type openFn func(string, string) (*process, proc.Thread, error)
   197  
   198  var openFns = []openFn{readLinuxOrPlatformIndependentCore, readAMD64Minidump}
   199  
   200  // ErrUnrecognizedFormat is returned when the core file is not recognized as
   201  // any of the supported formats.
   202  var ErrUnrecognizedFormat = errors.New("unrecognized core format")
   203  
   204  // OpenCore will open the core file and return a *proc.TargetGroup.
   205  // If the DWARF information cannot be found in the binary, Delve will look
   206  // for external debug files in the directories passed in.
   207  func OpenCore(corePath, exePath string, debugInfoDirs []string) (*proc.TargetGroup, error) {
   208  	var p *process
   209  	var currentThread proc.Thread
   210  	var err error
   211  	for _, openFn := range openFns {
   212  		p, currentThread, err = openFn(corePath, exePath)
   213  		if err != ErrUnrecognizedFormat {
   214  			break
   215  		}
   216  	}
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	if currentThread == nil {
   222  		return nil, ErrNoThreads
   223  	}
   224  
   225  	grp, addTarget := proc.NewGroup(p, proc.NewTargetGroupConfig{
   226  		DebugInfoDirs:       debugInfoDirs,
   227  		DisableAsyncPreempt: false,
   228  		CanDump:             false,
   229  	})
   230  	_, err = addTarget(p, p.pid, currentThread, exePath, proc.StopAttached, "")
   231  	return grp, err
   232  }
   233  
   234  // BinInfo will return the binary info.
   235  func (p *process) BinInfo() *proc.BinaryInfo {
   236  	return p.bi
   237  }
   238  
   239  // EntryPoint will return the entry point address for this core file.
   240  func (p *process) EntryPoint() (uint64, error) {
   241  	return p.entryPoint, nil
   242  }
   243  
   244  // WriteBreakpoint is a noop function since you
   245  // cannot write breakpoints into core files.
   246  func (p *process) WriteBreakpoint(*proc.Breakpoint) error {
   247  	return errors.New("cannot write a breakpoint to a core file")
   248  }
   249  
   250  // Recorded returns whether this is a live or recorded process. Always returns true for core files.
   251  func (p *process) Recorded() (bool, string) { return true, "" }
   252  
   253  // Restart will only return an error for core files, as they are not executing.
   254  func (p *process) Restart(*proc.ContinueOnceContext, string) (proc.Thread, error) {
   255  	return nil, ErrContinueCore
   256  }
   257  
   258  // ChangeDirection will only return an error as you cannot continue a core process.
   259  func (p *process) ChangeDirection(proc.Direction) error { return ErrContinueCore }
   260  
   261  // GetDirection will always return forward.
   262  func (p *process) GetDirection() proc.Direction { return proc.Forward }
   263  
   264  // When does not apply to core files, it is to support the Mozilla 'rr' backend.
   265  func (p *process) When() (string, error) { return "", nil }
   266  
   267  // Checkpoint for core files returns an error, there is no execution of a core file.
   268  func (p *process) Checkpoint(string) (int, error) { return -1, ErrContinueCore }
   269  
   270  // Checkpoints returns nil on core files, you cannot set checkpoints when debugging core files.
   271  func (p *process) Checkpoints() ([]proc.Checkpoint, error) { return nil, nil }
   272  
   273  // ClearCheckpoint clears a checkpoint, but will only return an error for core files.
   274  func (p *process) ClearCheckpoint(int) error { return errors.New("checkpoint not found") }
   275  
   276  func (p *process) SupportsBPF() bool {
   277  	return false
   278  }
   279  
   280  func (p *process) SetUProbe(fnName string, goidOffset int64, args []ebpf.UProbeArgMap) error {
   281  	panic("not implemented")
   282  }
   283  
   284  // StartCallInjection notifies the backend that we are about to inject a function call.
   285  func (p *process) StartCallInjection() (func(), error) { return func() {}, nil }
   286  
   287  func (p *process) EnableURetProbes() error {
   288  	panic("not implemented")
   289  }
   290  
   291  func (p *process) DisableURetProbes() error {
   292  	panic("not implemented")
   293  }
   294  
   295  // ReadMemory will return memory from the core file at the specified location and put the
   296  // read memory into `data`, returning the length read, and returning an error if
   297  // the length read is shorter than the length of the `data` buffer.
   298  func (p *process) ReadMemory(data []byte, addr uint64) (n int, err error) {
   299  	n, err = p.mem.ReadMemory(data, addr)
   300  	if err == nil && n != len(data) {
   301  		err = ErrShortRead
   302  	}
   303  	return n, err
   304  }
   305  
   306  // WriteMemory will only return an error for core files, you cannot write
   307  // to the memory of a core process.
   308  func (p *process) WriteMemory(addr uint64, data []byte) (int, error) {
   309  	return 0, ErrWriteCore
   310  }
   311  
   312  // FollowExec enables (or disables) follow exec mode
   313  func (p *process) FollowExec(bool) error {
   314  	return nil
   315  }
   316  
   317  // ProcessMemory returns the memory of this thread's process.
   318  func (t *thread) ProcessMemory() proc.MemoryReadWriter {
   319  	return t.p
   320  }
   321  
   322  // Location returns the location of this thread based on
   323  // the value of the instruction pointer register.
   324  func (t *thread) Location() (*proc.Location, error) {
   325  	regs, err := t.th.registers()
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	pc := regs.PC()
   330  	f, l, fn := t.p.bi.PCToLine(pc)
   331  	return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
   332  }
   333  
   334  // Breakpoint returns the current breakpoint this thread is stopped at.
   335  // For core files this always returns an empty BreakpointState struct, as
   336  // there are no breakpoints when debugging core files.
   337  func (t *thread) Breakpoint() *proc.BreakpointState {
   338  	return &proc.BreakpointState{}
   339  }
   340  
   341  // ThreadID returns the ID for this thread.
   342  func (t *thread) ThreadID() int {
   343  	return t.th.pid()
   344  }
   345  
   346  // Registers returns the current value of the registers for this thread.
   347  func (t *thread) Registers() (proc.Registers, error) {
   348  	return t.th.registers()
   349  }
   350  
   351  // RestoreRegisters will only return an error for core files,
   352  // you cannot change register values for core files.
   353  func (t *thread) RestoreRegisters(proc.Registers) error {
   354  	return ErrChangeRegisterCore
   355  }
   356  
   357  // BinInfo returns information about the binary.
   358  func (t *thread) BinInfo() *proc.BinaryInfo {
   359  	return t.p.bi
   360  }
   361  
   362  // SetCurrentBreakpoint will always just return nil
   363  // for core files, as there are no breakpoints in core files.
   364  func (t *thread) SetCurrentBreakpoint(adjustPC bool) error {
   365  	return nil
   366  }
   367  
   368  // SoftExc returns true if this thread received a software exception during the last resume.
   369  func (t *thread) SoftExc() bool {
   370  	return false
   371  }
   372  
   373  // Common returns a struct containing common information
   374  // across thread implementations.
   375  func (t *thread) Common() *proc.CommonThread {
   376  	return &t.common
   377  }
   378  
   379  // SetPC will always return an error, you cannot
   380  // change register values when debugging core files.
   381  func (t *thread) SetPC(uint64) error {
   382  	return ErrChangeRegisterCore
   383  }
   384  
   385  // SetSP will always return an error, you cannot
   386  // change register values when debugging core files.
   387  func (t *thread) SetSP(uint64) error {
   388  	return ErrChangeRegisterCore
   389  }
   390  
   391  // SetDX will always return an error, you cannot
   392  // change register values when debugging core files.
   393  func (t *thread) SetDX(uint64) error {
   394  	return ErrChangeRegisterCore
   395  }
   396  
   397  // SetReg will always return an error, you cannot
   398  // change register values when debugging core files.
   399  func (t *thread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
   400  	return ErrChangeRegisterCore
   401  }
   402  
   403  // Breakpoints will return all breakpoints for the process.
   404  func (p *process) Breakpoints() *proc.BreakpointMap {
   405  	return &p.breakpoints
   406  }
   407  
   408  // EraseBreakpoint will always return an error as you cannot set or clear
   409  // breakpoints on core files.
   410  func (p *process) EraseBreakpoint(bp *proc.Breakpoint) error {
   411  	return proc.NoBreakpointError{Addr: bp.Addr}
   412  }
   413  
   414  // ClearInternalBreakpoints will always return nil and have no
   415  // effect since you cannot set breakpoints on core files.
   416  func (p *process) ClearInternalBreakpoints() error {
   417  	return nil
   418  }
   419  
   420  func (*process) ContinueOnce(cctx *proc.ContinueOnceContext) (proc.Thread, proc.StopReason, error) {
   421  	return nil, proc.StopUnknown, ErrContinueCore
   422  }
   423  
   424  // StepInstruction will always return an error
   425  // as you cannot control execution of a core file.
   426  func (p *process) StepInstruction(int) error {
   427  	return ErrContinueCore
   428  }
   429  
   430  // RequestManualStop will return nil and have no effect
   431  // as you cannot control execution of a core file.
   432  func (p *process) RequestManualStop(cctx *proc.ContinueOnceContext) error {
   433  	return nil
   434  }
   435  
   436  // CheckAndClearManualStopRequest will always return false and
   437  // have no effect since there are no manual stop requests as
   438  // there is no controlling execution of a core file.
   439  func (p *process) CheckAndClearManualStopRequest() bool {
   440  	return false
   441  }
   442  
   443  // Memory returns the process memory.
   444  func (p *process) Memory() proc.MemoryReadWriter {
   445  	return p
   446  }
   447  
   448  // Detach will always return nil and have no
   449  // effect as you cannot detach from a core file
   450  // and have it continue execution or exit.
   451  func (p *process) Detach(int, bool) error {
   452  	return nil
   453  }
   454  
   455  func (p *process) Close() error {
   456  	return nil
   457  }
   458  
   459  // Valid returns whether the process is active. Always returns true
   460  // for core files as it cannot exit or be otherwise detached from.
   461  func (p *process) Valid() (bool, error) {
   462  	return true, nil
   463  }
   464  
   465  // ResumeNotify is a no-op on core files as we cannot
   466  // control execution.
   467  func (p *process) ResumeNotify(chan<- struct{}) {
   468  }
   469  
   470  // ThreadList will return a list of all threads currently in the process.
   471  func (p *process) ThreadList() []proc.Thread {
   472  	r := make([]proc.Thread, 0, len(p.Threads))
   473  	for _, v := range p.Threads {
   474  		r = append(r, v)
   475  	}
   476  	return r
   477  }
   478  
   479  // FindThread will return the thread with the corresponding thread ID.
   480  func (p *process) FindThread(threadID int) (proc.Thread, bool) {
   481  	t, ok := p.Threads[threadID]
   482  	return t, ok
   483  }
   484  
   485  func (p *process) MemoryMap() ([]proc.MemoryMapEntry, error) {
   486  	return nil, proc.ErrMemoryMapNotSupported
   487  }
   488  
   489  func (p *process) DumpProcessNotes(notes []elfwriter.Note, threadDone func()) (threadsDone bool, out []elfwriter.Note, err error) {
   490  	return false, notes, nil
   491  }
   492  
   493  func (p *process) GetBufferedTracepoints() []ebpf.RawUProbeParams {
   494  	return nil
   495  }