delve/pkg/proc/native/dump_linux_amd64.go

153 lines
3.7 KiB
Go

package native
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"os"
"path/filepath"
"github.com/go-delve/delve/pkg/elfwriter"
"github.com/go-delve/delve/pkg/proc/linutil"
"golang.org/x/sys/unix"
)
const _NT_AUXV elf.NType = 0x6
type linuxPrPsInfo struct {
State uint8
Sname int8
Zomb uint8
Nice int8
_ [4]uint8
Flag uint64
Uid, Gid uint32
Pid, Ppid, Pgrp, Sid int32
Fname [16]uint8
Args [80]uint8
}
func (p *nativeProcess) DumpProcessNotes(notes []elfwriter.Note, threadDone func()) (threadsDone bool, out []elfwriter.Note, err error) {
tobytes := func(x interface{}) []byte {
out := new(bytes.Buffer)
_ = binary.Write(out, binary.LittleEndian, x)
return out.Bytes()
}
prpsinfo := linuxPrPsInfo{
Pid: int32(p.pid),
}
fname := p.os.comm
if len(fname) > len(prpsinfo.Fname)-1 {
fname = fname[:len(prpsinfo.Fname)-1]
}
copy(prpsinfo.Fname[:], fname)
prpsinfo.Fname[len(fname)] = 0
if cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", p.pid)); err == nil {
for len(cmdline) > 0 && cmdline[len(cmdline)-1] == '\n' {
cmdline = cmdline[:len(cmdline)-1]
}
if zero := bytes.Index(cmdline, []byte{0}); zero >= 0 {
cmdline = cmdline[zero+1:]
}
path := p.BinInfo().Images[0].Path
if abs, err := filepath.Abs(path); err == nil {
path = abs
}
args := make([]byte, 0, len(path)+len(cmdline)+1)
args = append(args, []byte(path)...)
args = append(args, 0)
args = append(args, cmdline...)
if len(args) > len(prpsinfo.Args)-1 {
args = args[:len(prpsinfo.Args)-1]
}
copy(prpsinfo.Args[:], args)
prpsinfo.Args[len(args)] = 0
}
notes = append(notes, elfwriter.Note{
Type: elf.NT_PRPSINFO,
Data: tobytes(prpsinfo),
})
auxvbuf, err := os.ReadFile(fmt.Sprintf("/proc/%d/auxv", p.pid))
if err == nil {
notes = append(notes, elfwriter.Note{
Type: _NT_AUXV,
Data: auxvbuf,
})
}
for _, th := range p.threads {
regs, err := th.Registers()
if err != nil {
return false, notes, err
}
regs, err = regs.Copy() // triggers floating point register load
if err != nil {
return false, notes, err
}
nregs := regs.(*linutil.AMD64Registers)
var prstatus linuxPrStatusAMD64
prstatus.Pid = int32(th.ID)
prstatus.Ppid = int32(p.pid)
prstatus.Pgrp = int32(p.pid)
prstatus.Sid = int32(p.pid)
prstatus.Reg = *(nregs.Regs)
notes = append(notes, elfwriter.Note{
Type: elf.NT_PRSTATUS,
Data: tobytes(prstatus),
})
var xsave []byte
if nregs.Fpregset != nil && nregs.Fpregset.Xsave != nil {
xsave = make([]byte, len(nregs.Fpregset.Xsave))
copy(xsave, nregs.Fpregset.Xsave)
} else {
xsave = make([]byte, 512+64) // XSAVE header start + XSAVE header length
}
// Even if we have the XSAVE area on some versions of linux (or some CPU
// models?) it won't contain the legacy x87 registers, so copy them over
// in case we got them from PTRACE_GETFPREGS.
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, &nregs.Fpregset.AMD64PtraceFpRegs)
copy(xsave, buf.Bytes())
notes = append(notes, elfwriter.Note{
Type: _NT_X86_XSTATE,
Data: xsave,
})
threadDone()
}
return true, notes, nil
}
type linuxPrStatusAMD64 struct {
Siginfo linuxSiginfo
Cursig uint16
_ [2]uint8
Sigpend uint64
Sighold uint64
Pid, Ppid, Pgrp, Sid int32
Utime, Stime, CUtime, CStime unix.Timeval
Reg linutil.AMD64PtraceRegs
Fpvalid int64
}
// LinuxSiginfo is a copy of the
// siginfo kernel struct.
type linuxSiginfo struct {
Signo int32
Code int32
Errno int32
}