File: dump_linux_amd64.go

package info (click to toggle)
delve 1.24.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 14,092 kB
  • sloc: ansic: 111,943; sh: 169; asm: 141; makefile: 43; python: 23
file content (152 lines) | stat: -rw-r--r-- 3,825 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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
}