-
Notifications
You must be signed in to change notification settings - Fork 652
/
vdso.go
143 lines (122 loc) · 3.77 KB
/
vdso.go
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
package internal
import (
"debug/elf"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"os"
"github.com/cilium/ebpf/internal/unix"
)
var (
errAuxvNoVDSO = errors.New("no vdso address found in auxv")
)
// vdsoVersion returns the LINUX_VERSION_CODE embedded in the vDSO library
// linked into the current process image.
func vdsoVersion() (uint32, error) {
av, err := newAuxvRuntimeReader()
if err != nil {
return 0, err
}
defer av.Close()
vdsoAddr, err := vdsoMemoryAddress(av)
if err != nil {
return 0, fmt.Errorf("finding vDSO memory address: %w", err)
}
// Use /proc/self/mem rather than unsafe.Pointer tricks.
mem, err := os.Open("/proc/self/mem")
if err != nil {
return 0, fmt.Errorf("opening mem: %w", err)
}
defer mem.Close()
// Open ELF at provided memory address, as offset into /proc/self/mem.
c, err := vdsoLinuxVersionCode(io.NewSectionReader(mem, int64(vdsoAddr), math.MaxInt64))
if err != nil {
return 0, fmt.Errorf("reading linux version code: %w", err)
}
return c, nil
}
// vdsoMemoryAddress returns the memory address of the vDSO library
// linked into the current process image. r is an io.Reader into an auxv blob.
func vdsoMemoryAddress(r auxvPairReader) (uintptr, error) {
// Loop through all tag/value pairs in auxv until we find `AT_SYSINFO_EHDR`,
// the address of a page containing the virtual Dynamic Shared Object (vDSO).
for {
tag, value, err := r.ReadAuxvPair()
if err != nil {
return 0, err
}
switch tag {
case _AT_SYSINFO_EHDR:
if value != 0 {
return uintptr(value), nil
}
return 0, fmt.Errorf("invalid vDSO address in auxv")
// _AT_NULL is always the last tag/val pair in the aux vector
// and can be treated like EOF.
case _AT_NULL:
return 0, errAuxvNoVDSO
}
}
}
// format described at https://www.man7.org/linux/man-pages/man5/elf.5.html in section 'Notes (Nhdr)'
type elfNoteHeader struct {
NameSize int32
DescSize int32
Type int32
}
// vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in
// the ELF notes section of the binary provided by the reader.
func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) {
hdr, err := NewSafeELFFile(r)
if err != nil {
return 0, fmt.Errorf("reading vDSO ELF: %w", err)
}
sections := hdr.SectionsByType(elf.SHT_NOTE)
if len(sections) == 0 {
return 0, fmt.Errorf("no note section found in vDSO ELF")
}
for _, sec := range sections {
sr := sec.Open()
var n elfNoteHeader
// Read notes until we find one named 'Linux'.
for {
if err := binary.Read(sr, hdr.ByteOrder, &n); err != nil {
if errors.Is(err, io.EOF) {
// We looked at all the notes in this section
break
}
return 0, fmt.Errorf("reading note header: %w", err)
}
// If a note name is defined, it follows the note header.
var name string
if n.NameSize > 0 {
// Read the note name, aligned to 4 bytes.
buf := make([]byte, Align(n.NameSize, 4))
if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil {
return 0, fmt.Errorf("reading note name: %w", err)
}
// Read nul-terminated string.
name = unix.ByteSliceToString(buf[:n.NameSize])
}
// If a note descriptor is defined, it follows the name.
// It is possible for a note to have a descriptor but not a name.
if n.DescSize > 0 {
// LINUX_VERSION_CODE is a uint32 value.
if name == "Linux" && n.DescSize == 4 && n.Type == 0 {
var version uint32
if err := binary.Read(sr, hdr.ByteOrder, &version); err != nil {
return 0, fmt.Errorf("reading note descriptor: %w", err)
}
return version, nil
}
// Discard the note descriptor if it exists but we're not interested in it.
if _, err := io.CopyN(io.Discard, sr, int64(Align(n.DescSize, 4))); err != nil {
return 0, err
}
}
}
}
return 0, fmt.Errorf("no Linux note in ELF")
}