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

     1  package core
     2  
     3  import (
     4  	"bytes"
     5  	"debug/elf"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"strings"
    11  
    12  	"gitlab.com/Raven-IO/raven-delve/pkg/elfwriter"
    13  	"gitlab.com/Raven-IO/raven-delve/pkg/proc"
    14  	"gitlab.com/Raven-IO/raven-delve/pkg/proc/amd64util"
    15  	"gitlab.com/Raven-IO/raven-delve/pkg/proc/linutil"
    16  )
    17  
    18  // Copied from golang.org/x/sys/unix.Timeval since it's not available on all
    19  // systems.
    20  type linuxCoreTimeval struct {
    21  	Sec  int64
    22  	Usec int64
    23  }
    24  
    25  // NT_FILE is file mapping information, e.g. program text mappings. Desc is a LinuxNTFile.
    26  const _NT_FILE elf.NType = 0x46494c45 // "FILE".
    27  
    28  // NT_X86_XSTATE is other registers, including AVX and such.
    29  const _NT_X86_XSTATE elf.NType = 0x202 // Note type for notes containing X86 XSAVE area.
    30  
    31  // NT_AUXV is the note type for notes containing a copy of the Auxv array
    32  const _NT_AUXV elf.NType = 0x6
    33  
    34  // NT_FPREGSET is the note type for floating point registers.
    35  const _NT_FPREGSET elf.NType = 0x2
    36  
    37  // Fetch architecture using exeELF.Machine from core file
    38  // Refer http://man7.org/linux/man-pages/man5/elf.5.html
    39  const (
    40  	_EM_AARCH64          = 183
    41  	_EM_X86_64           = 62
    42  	_ARM_FP_HEADER_START = 512
    43  )
    44  
    45  const elfErrorBadMagicNumber = "bad magic number"
    46  
    47  func linuxThreadsFromNotes(p *process, notes []*note, machineType elf.Machine) proc.Thread {
    48  	var currentThread proc.Thread
    49  	var lastThreadAMD *linuxAMD64Thread
    50  	var lastThreadARM *linuxARM64Thread
    51  	for _, note := range notes {
    52  		switch note.Type {
    53  		case elf.NT_PRSTATUS:
    54  			if machineType == _EM_X86_64 {
    55  				t := note.Desc.(*linuxPrStatusAMD64)
    56  				lastThreadAMD = &linuxAMD64Thread{linutil.AMD64Registers{Regs: &t.Reg}, t}
    57  				p.Threads[int(t.Pid)] = &thread{lastThreadAMD, p, proc.CommonThread{}}
    58  				if currentThread == nil {
    59  					currentThread = p.Threads[int(t.Pid)]
    60  				}
    61  			} else if machineType == _EM_AARCH64 {
    62  				t := note.Desc.(*linuxPrStatusARM64)
    63  				lastThreadARM = &linuxARM64Thread{linutil.ARM64Registers{Regs: &t.Reg}, t}
    64  				p.Threads[int(t.Pid)] = &thread{lastThreadARM, p, proc.CommonThread{}}
    65  				if currentThread == nil {
    66  					currentThread = p.Threads[int(t.Pid)]
    67  				}
    68  			}
    69  		case _NT_FPREGSET:
    70  			if machineType == _EM_AARCH64 {
    71  				if lastThreadARM != nil {
    72  					lastThreadARM.regs.Fpregs = note.Desc.(*linutil.ARM64PtraceFpRegs).Decode()
    73  				}
    74  			}
    75  		case _NT_X86_XSTATE:
    76  			if machineType == _EM_X86_64 {
    77  				if lastThreadAMD != nil {
    78  					lastThreadAMD.regs.Fpregs = note.Desc.(*amd64util.AMD64Xstate).Decode()
    79  				}
    80  			}
    81  		case elf.NT_PRPSINFO:
    82  			p.pid = int(note.Desc.(*linuxPrPsInfo).Pid)
    83  		}
    84  	}
    85  	return currentThread
    86  }
    87  
    88  // readLinuxOrPlatformIndependentCore reads a core file from corePath
    89  // corresponding to the executable at exePath. For details on the Linux ELF
    90  // core format, see:
    91  // http://www.gabriel.urdhr.fr/2015/05/29/core-file/,
    92  // http://uhlo.blogspot.fr/2012/05/brief-look-into-core-dumps.html,
    93  // elf_core_dump in http://lxr.free-electrons.com/source/fs/binfmt_elf.c,
    94  // and, if absolutely desperate, readelf.c from the binutils source.
    95  func readLinuxOrPlatformIndependentCore(corePath, exePath string) (*process, proc.Thread, error) {
    96  	coreFile, err := elf.Open(corePath)
    97  	if err != nil {
    98  		if _, isfmterr := err.(*elf.FormatError); isfmterr && (strings.Contains(err.Error(), elfErrorBadMagicNumber) || strings.Contains(err.Error(), " at offset 0x0: too short")) {
    99  			// Go >=1.11 and <1.11 produce different errors when reading a non-elf file.
   100  			return nil, nil, ErrUnrecognizedFormat
   101  		}
   102  		return nil, nil, err
   103  	}
   104  
   105  	if coreFile.Type != elf.ET_CORE {
   106  		return nil, nil, fmt.Errorf("%v is not a core file", coreFile)
   107  	}
   108  
   109  	machineType := coreFile.Machine
   110  	notes, platformIndependentDelveCore, err := readNotes(coreFile, machineType)
   111  	if err != nil {
   112  		return nil, nil, err
   113  	}
   114  
   115  	exe, err := os.Open(exePath)
   116  	if err != nil {
   117  		return nil, nil, err
   118  	}
   119  	exeELF, err := elf.NewFile(exe)
   120  	if err != nil {
   121  		if !platformIndependentDelveCore {
   122  			return nil, nil, err
   123  		}
   124  	} else {
   125  		if exeELF.Machine != machineType {
   126  			return nil, nil, fmt.Errorf("architecture mismatch between core file (%#x) and executable file (%#x)", machineType, exeELF.Machine)
   127  		}
   128  		if exeELF.Type != elf.ET_EXEC && exeELF.Type != elf.ET_DYN {
   129  			return nil, nil, fmt.Errorf("%v is not an exe file", exeELF)
   130  		}
   131  	}
   132  
   133  	memory := buildMemory(coreFile, exeELF, exe, notes)
   134  
   135  	// TODO support 386
   136  	var bi *proc.BinaryInfo
   137  	if platformIndependentDelveCore {
   138  		goos, goarch, err := platformFromNotes(notes)
   139  		if err != nil {
   140  			return nil, nil, err
   141  		}
   142  		bi = proc.NewBinaryInfo(goos, goarch)
   143  	} else {
   144  		switch machineType {
   145  		case _EM_X86_64:
   146  			bi = proc.NewBinaryInfo("linux", "amd64")
   147  		case _EM_AARCH64:
   148  			bi = proc.NewBinaryInfo("linux", "arm64")
   149  		default:
   150  			return nil, nil, fmt.Errorf("unsupported machine type")
   151  		}
   152  	}
   153  
   154  	entryPoint := findEntryPoint(notes, bi.Arch.PtrSize())
   155  
   156  	p := &process{
   157  		mem:         memory,
   158  		Threads:     map[int]*thread{},
   159  		entryPoint:  entryPoint,
   160  		bi:          bi,
   161  		breakpoints: proc.NewBreakpointMap(),
   162  	}
   163  
   164  	if platformIndependentDelveCore {
   165  		currentThread, err := threadsFromDelveNotes(p, notes)
   166  		return p, currentThread, err
   167  	}
   168  
   169  	currentThread := linuxThreadsFromNotes(p, notes, machineType)
   170  	return p, currentThread, nil
   171  }
   172  
   173  type linuxAMD64Thread struct {
   174  	regs linutil.AMD64Registers
   175  	t    *linuxPrStatusAMD64
   176  }
   177  
   178  type linuxARM64Thread struct {
   179  	regs linutil.ARM64Registers
   180  	t    *linuxPrStatusARM64
   181  }
   182  
   183  func (t *linuxAMD64Thread) registers() (proc.Registers, error) {
   184  	var r linutil.AMD64Registers
   185  	r.Regs = t.regs.Regs
   186  	r.Fpregs = t.regs.Fpregs
   187  	return &r, nil
   188  }
   189  
   190  func (t *linuxARM64Thread) registers() (proc.Registers, error) {
   191  	var r linutil.ARM64Registers
   192  	r.Regs = t.regs.Regs
   193  	r.Fpregs = t.regs.Fpregs
   194  	return &r, nil
   195  }
   196  
   197  func (t *linuxAMD64Thread) pid() int {
   198  	return int(t.t.Pid)
   199  }
   200  
   201  func (t *linuxARM64Thread) pid() int {
   202  	return int(t.t.Pid)
   203  }
   204  
   205  // Note is a note from the PT_NOTE prog.
   206  // Relevant types:
   207  // - NT_FILE: File mapping information, e.g. program text mappings. Desc is a LinuxNTFile.
   208  // - NT_PRPSINFO: Information about a process, including PID and signal. Desc is a LinuxPrPsInfo.
   209  // - NT_PRSTATUS: Information about a thread, including base registers, state, etc. Desc is a LinuxPrStatus.
   210  // - NT_FPREGSET (Not implemented): x87 floating point registers.
   211  // - NT_X86_XSTATE: Other registers, including AVX and such.
   212  type note struct {
   213  	Type elf.NType
   214  	Name string
   215  	Desc interface{} // Decoded Desc from the
   216  }
   217  
   218  // readNotes reads all the notes from the notes prog in core.
   219  func readNotes(core *elf.File, machineType elf.Machine) ([]*note, bool, error) {
   220  	var notesProg *elf.Prog
   221  	for _, prog := range core.Progs {
   222  		if prog.Type == elf.PT_NOTE {
   223  			notesProg = prog
   224  			break
   225  		}
   226  	}
   227  
   228  	r := notesProg.Open()
   229  	hasDelveThread := false
   230  	hasDelveHeader := false
   231  	hasElfPrStatus := false
   232  	notes := []*note{}
   233  	for {
   234  		note, err := readNote(r, machineType)
   235  		if err == io.EOF {
   236  			break
   237  		}
   238  		if err != nil {
   239  			return nil, false, err
   240  		}
   241  		switch note.Type {
   242  		case elfwriter.DelveHeaderNoteType:
   243  			hasDelveHeader = true
   244  		case elfwriter.DelveThreadNodeType:
   245  			hasDelveThread = true
   246  		case elf.NT_PRSTATUS:
   247  			hasElfPrStatus = true
   248  		}
   249  		notes = append(notes, note)
   250  	}
   251  
   252  	return notes, hasDelveThread && hasDelveHeader && !hasElfPrStatus, nil
   253  }
   254  
   255  // readNote reads a single note from r, decoding the descriptor if possible.
   256  func readNote(r io.ReadSeeker, machineType elf.Machine) (*note, error) {
   257  	// Notes are laid out as described in the SysV ABI:
   258  	// http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section
   259  	note := &note{}
   260  	hdr := &elfNotesHdr{}
   261  
   262  	err := binary.Read(r, binary.LittleEndian, hdr)
   263  	if err != nil {
   264  		return nil, err // don't wrap so readNotes sees EOF.
   265  	}
   266  	note.Type = elf.NType(hdr.Type)
   267  
   268  	name := make([]byte, hdr.Namesz)
   269  	if _, err := r.Read(name); err != nil {
   270  		return nil, fmt.Errorf("reading name: %v", err)
   271  	}
   272  	note.Name = string(name)
   273  	if err := skipPadding(r, 4); err != nil {
   274  		return nil, fmt.Errorf("aligning after name: %v", err)
   275  	}
   276  	desc := make([]byte, hdr.Descsz)
   277  	if _, err := r.Read(desc); err != nil {
   278  		return nil, fmt.Errorf("reading desc: %v", err)
   279  	}
   280  	descReader := bytes.NewReader(desc)
   281  	switch note.Type {
   282  	case elf.NT_PRSTATUS:
   283  		switch machineType {
   284  		case _EM_X86_64:
   285  			note.Desc = &linuxPrStatusAMD64{}
   286  		case _EM_AARCH64:
   287  			note.Desc = &linuxPrStatusARM64{}
   288  		default:
   289  			return nil, fmt.Errorf("unsupported machine type")
   290  		}
   291  		if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil {
   292  			return nil, fmt.Errorf("reading NT_PRSTATUS: %v", err)
   293  		}
   294  	case elf.NT_PRPSINFO:
   295  		note.Desc = &linuxPrPsInfo{}
   296  		if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil {
   297  			return nil, fmt.Errorf("reading NT_PRPSINFO: %v", err)
   298  		}
   299  	case _NT_FILE:
   300  		// No good documentation reference, but the structure is
   301  		// simply a header, including entry count, followed by that
   302  		// many entries, and then the file name of each entry,
   303  		// null-delimited. Not reading the names here.
   304  		data := &linuxNTFile{}
   305  		if err := binary.Read(descReader, binary.LittleEndian, &data.linuxNTFileHdr); err != nil {
   306  			return nil, fmt.Errorf("reading NT_FILE header: %v", err)
   307  		}
   308  		for i := 0; i < int(data.Count); i++ {
   309  			entry := &linuxNTFileEntry{}
   310  			if err := binary.Read(descReader, binary.LittleEndian, entry); err != nil {
   311  				return nil, fmt.Errorf("reading NT_FILE entry %v: %v", i, err)
   312  			}
   313  			data.entries = append(data.entries, entry)
   314  		}
   315  		note.Desc = data
   316  	case _NT_X86_XSTATE:
   317  		if machineType == _EM_X86_64 {
   318  			var fpregs amd64util.AMD64Xstate
   319  			if err := amd64util.AMD64XstateRead(desc, true, &fpregs); err != nil {
   320  				return nil, err
   321  			}
   322  			note.Desc = &fpregs
   323  		}
   324  	case _NT_AUXV, elfwriter.DelveHeaderNoteType, elfwriter.DelveThreadNodeType:
   325  		note.Desc = desc
   326  	case _NT_FPREGSET:
   327  		if machineType == _EM_AARCH64 {
   328  			fpregs := &linutil.ARM64PtraceFpRegs{}
   329  			rdr := bytes.NewReader(desc[:_ARM_FP_HEADER_START])
   330  			if err := binary.Read(rdr, binary.LittleEndian, fpregs.Byte()); err != nil {
   331  				return nil, err
   332  			}
   333  			note.Desc = fpregs
   334  		}
   335  	}
   336  	if err := skipPadding(r, 4); err != nil {
   337  		return nil, fmt.Errorf("aligning after desc: %v", err)
   338  	}
   339  	return note, nil
   340  }
   341  
   342  // skipPadding moves r to the next multiple of pad.
   343  func skipPadding(r io.ReadSeeker, pad int64) error {
   344  	pos, err := r.Seek(0, io.SeekCurrent)
   345  	if err != nil {
   346  		return err
   347  	}
   348  	if pos%pad == 0 {
   349  		return nil
   350  	}
   351  	if _, err := r.Seek(pad-(pos%pad), io.SeekCurrent); err != nil {
   352  		return err
   353  	}
   354  	return nil
   355  }
   356  
   357  func buildMemory(core, exeELF *elf.File, exe io.ReaderAt, notes []*note) proc.MemoryReader {
   358  	memory := &SplicedMemory{}
   359  
   360  	// For now, assume all file mappings are to the exe.
   361  	for _, note := range notes {
   362  		if note.Type == _NT_FILE {
   363  			fileNote := note.Desc.(*linuxNTFile)
   364  			for _, entry := range fileNote.entries {
   365  				r := &offsetReaderAt{
   366  					reader: exe,
   367  					offset: entry.Start - (entry.FileOfs * fileNote.PageSize),
   368  				}
   369  				memory.Add(r, entry.Start, entry.End-entry.Start)
   370  			}
   371  
   372  		}
   373  	}
   374  
   375  	// Load memory segments from exe and then from the core file,
   376  	// allowing the corefile to overwrite previously loaded segments
   377  	for _, elfFile := range []*elf.File{exeELF, core} {
   378  		if elfFile == nil {
   379  			continue
   380  		}
   381  		for _, prog := range elfFile.Progs {
   382  			if prog.Type == elf.PT_LOAD {
   383  				if prog.Filesz == 0 {
   384  					continue
   385  				}
   386  				r := &offsetReaderAt{
   387  					reader: prog.ReaderAt,
   388  					offset: prog.Vaddr,
   389  				}
   390  				memory.Add(r, prog.Vaddr, prog.Filesz)
   391  			}
   392  		}
   393  	}
   394  	return memory
   395  }
   396  
   397  func findEntryPoint(notes []*note, ptrSize int) uint64 {
   398  	for _, note := range notes {
   399  		if note.Type == _NT_AUXV {
   400  			return linutil.EntryPointFromAuxv(note.Desc.([]byte), ptrSize)
   401  		}
   402  	}
   403  	return 0
   404  }
   405  
   406  // LinuxPrPsInfo has various structures from the ELF spec and the Linux kernel.
   407  // AMD64 specific primarily because of unix.PtraceRegs, but also
   408  // because some of the fields are word sized.
   409  // See http://lxr.free-electrons.com/source/include/uapi/linux/elfcore.h
   410  type linuxPrPsInfo struct {
   411  	State                uint8
   412  	Sname                int8
   413  	Zomb                 uint8
   414  	Nice                 int8
   415  	_                    [4]uint8
   416  	Flag                 uint64
   417  	Uid, Gid             uint32
   418  	Pid, Ppid, Pgrp, Sid int32
   419  	Fname                [16]uint8
   420  	Args                 [80]uint8
   421  }
   422  
   423  // LinuxPrStatusAMD64 is a copy of the prstatus kernel struct.
   424  type linuxPrStatusAMD64 struct {
   425  	Siginfo                      linuxSiginfo
   426  	Cursig                       uint16
   427  	_                            [2]uint8
   428  	Sigpend                      uint64
   429  	Sighold                      uint64
   430  	Pid, Ppid, Pgrp, Sid         int32
   431  	Utime, Stime, CUtime, CStime linuxCoreTimeval
   432  	Reg                          linutil.AMD64PtraceRegs
   433  	Fpvalid                      int32
   434  }
   435  
   436  // LinuxPrStatusARM64 is a copy of the prstatus kernel struct.
   437  type linuxPrStatusARM64 struct {
   438  	Siginfo                      linuxSiginfo
   439  	Cursig                       uint16
   440  	_                            [2]uint8
   441  	Sigpend                      uint64
   442  	Sighold                      uint64
   443  	Pid, Ppid, Pgrp, Sid         int32
   444  	Utime, Stime, CUtime, CStime linuxCoreTimeval
   445  	Reg                          linutil.ARM64PtraceRegs
   446  	Fpvalid                      int32
   447  }
   448  
   449  // LinuxSiginfo is a copy of the
   450  // siginfo kernel struct.
   451  type linuxSiginfo struct {
   452  	Signo int32
   453  	Code  int32
   454  	Errno int32
   455  }
   456  
   457  // LinuxNTFile contains information on mapped files.
   458  type linuxNTFile struct {
   459  	linuxNTFileHdr
   460  	entries []*linuxNTFileEntry
   461  }
   462  
   463  // LinuxNTFileHdr is a header struct for NTFile.
   464  type linuxNTFileHdr struct {
   465  	Count    uint64
   466  	PageSize uint64
   467  }
   468  
   469  // LinuxNTFileEntry is an entry of an NT_FILE note.
   470  type linuxNTFileEntry struct {
   471  	Start   uint64
   472  	End     uint64
   473  	FileOfs uint64
   474  }
   475  
   476  // elfNotesHdr is the ELF Notes header.
   477  // Same size on 64 and 32-bit machines.
   478  type elfNotesHdr struct {
   479  	Namesz uint32
   480  	Descsz uint32
   481  	Type   uint32
   482  }