1 package internal
2
3 import (
4 "debug/elf"
5 "encoding/binary"
6 "errors"
7 "fmt"
8 "io"
9 "math"
10 "os"
11
12 "github.com/cilium/ebpf/internal/unix"
13 )
14
15 var (
16 errAuxvNoVDSO = errors.New("no vdso address found in auxv")
17 )
18
19
20
21 func vdsoVersion() (uint32, error) {
22
23
24
25 av, err := os.Open("/proc/self/auxv")
26 if err != nil {
27 return 0, fmt.Errorf("opening auxv: %w", err)
28 }
29 defer av.Close()
30
31 vdsoAddr, err := vdsoMemoryAddress(av)
32 if err != nil {
33 return 0, fmt.Errorf("finding vDSO memory address: %w", err)
34 }
35
36
37 mem, err := os.Open("/proc/self/mem")
38 if err != nil {
39 return 0, fmt.Errorf("opening mem: %w", err)
40 }
41 defer mem.Close()
42
43
44 c, err := vdsoLinuxVersionCode(io.NewSectionReader(mem, int64(vdsoAddr), math.MaxInt64))
45 if err != nil {
46 return 0, fmt.Errorf("reading linux version code: %w", err)
47 }
48
49 return c, nil
50 }
51
52
53
54 func vdsoMemoryAddress(r io.Reader) (uint64, error) {
55 const (
56 _AT_NULL = 0
57 _AT_SYSINFO_EHDR = 33
58 )
59
60
61
62 aux := struct{ Tag, Val uint64 }{}
63 for {
64 if err := binary.Read(r, NativeEndian, &aux); err != nil {
65 return 0, fmt.Errorf("reading auxv entry: %w", err)
66 }
67
68 switch aux.Tag {
69 case _AT_SYSINFO_EHDR:
70 if aux.Val != 0 {
71 return aux.Val, nil
72 }
73 return 0, fmt.Errorf("invalid vDSO address in auxv")
74
75
76 case _AT_NULL:
77 return 0, errAuxvNoVDSO
78 }
79 }
80 }
81
82
83 type elfNoteHeader struct {
84 NameSize int32
85 DescSize int32
86 Type int32
87 }
88
89
90
91 func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) {
92 hdr, err := NewSafeELFFile(r)
93 if err != nil {
94 return 0, fmt.Errorf("reading vDSO ELF: %w", err)
95 }
96
97 sections := hdr.SectionsByType(elf.SHT_NOTE)
98 if len(sections) == 0 {
99 return 0, fmt.Errorf("no note section found in vDSO ELF")
100 }
101
102 for _, sec := range sections {
103 sr := sec.Open()
104 var n elfNoteHeader
105
106
107 for {
108 if err := binary.Read(sr, hdr.ByteOrder, &n); err != nil {
109 if errors.Is(err, io.EOF) {
110
111 break
112 }
113 return 0, fmt.Errorf("reading note header: %w", err)
114 }
115
116
117 var name string
118 if n.NameSize > 0 {
119
120 buf := make([]byte, Align(int(n.NameSize), 4))
121 if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil {
122 return 0, fmt.Errorf("reading note name: %w", err)
123 }
124
125
126 name = unix.ByteSliceToString(buf[:n.NameSize])
127 }
128
129
130
131 if n.DescSize > 0 {
132
133 if name == "Linux" && n.DescSize == 4 && n.Type == 0 {
134 var version uint32
135 if err := binary.Read(sr, hdr.ByteOrder, &version); err != nil {
136 return 0, fmt.Errorf("reading note descriptor: %w", err)
137 }
138 return version, nil
139 }
140
141
142 if _, err := io.CopyN(io.Discard, sr, int64(Align(int(n.DescSize), 4))); err != nil {
143 return 0, err
144 }
145 }
146 }
147 }
148
149 return 0, fmt.Errorf("no Linux note in ELF")
150 }
151
View as plain text