-
Notifications
You must be signed in to change notification settings - Fork 737
/
Copy pathkernel.go
170 lines (138 loc) · 3.84 KB
/
kernel.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package btf
import (
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/linux"
"github.com/cilium/ebpf/internal/platform"
)
var kernelBTF = struct {
sync.RWMutex
kernel *Spec
modules map[string]*Spec
}{
modules: make(map[string]*Spec),
}
// FlushKernelSpec removes any cached kernel type information.
func FlushKernelSpec() {
kernelBTF.Lock()
defer kernelBTF.Unlock()
kernelBTF.kernel = nil
kernelBTF.modules = make(map[string]*Spec)
}
// LoadKernelSpec returns the current kernel's BTF information.
//
// Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system
// for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled.
func LoadKernelSpec() (*Spec, error) {
kernelBTF.RLock()
spec := kernelBTF.kernel
kernelBTF.RUnlock()
if spec == nil {
kernelBTF.Lock()
defer kernelBTF.Unlock()
spec = kernelBTF.kernel
}
if spec != nil {
return spec.Copy(), nil
}
spec, _, err := loadKernelSpec()
if err != nil {
return nil, err
}
kernelBTF.kernel = spec
return spec.Copy(), nil
}
// LoadKernelModuleSpec returns the BTF information for the named kernel module.
//
// Defaults to /sys/kernel/btf/<module>.
// Returns an error wrapping ErrNotSupported if BTF is not enabled.
// Returns an error wrapping fs.ErrNotExist if BTF for the specific module doesn't exist.
func LoadKernelModuleSpec(module string) (*Spec, error) {
kernelBTF.RLock()
spec := kernelBTF.modules[module]
kernelBTF.RUnlock()
if spec != nil {
return spec.Copy(), nil
}
base, err := LoadKernelSpec()
if err != nil {
return nil, fmt.Errorf("load kernel spec: %w", err)
}
kernelBTF.Lock()
defer kernelBTF.Unlock()
if spec = kernelBTF.modules[module]; spec != nil {
return spec.Copy(), nil
}
spec, err = loadKernelModuleSpec(module, base)
if err != nil {
return nil, fmt.Errorf("load kernel module: %w", err)
}
kernelBTF.modules[module] = spec
return spec.Copy(), nil
}
func loadKernelSpec() (_ *Spec, fallback bool, _ error) {
if platform.IsWindows {
return nil, false, internal.ErrNotSupportedOnOS
}
fh, err := os.Open("/sys/kernel/btf/vmlinux")
if err == nil {
defer fh.Close()
spec, err := loadRawSpec(fh, internal.NativeEndian, nil)
return spec, false, err
}
file, err := findVMLinux()
if err != nil {
return nil, false, err
}
defer file.Close()
spec, err := LoadSpecFromReader(file)
return spec, true, err
}
func loadKernelModuleSpec(module string, base *Spec) (*Spec, error) {
if platform.IsWindows {
return nil, internal.ErrNotSupportedOnOS
}
dir, file := filepath.Split(module)
if dir != "" || filepath.Ext(file) != "" {
return nil, fmt.Errorf("invalid module name %q", module)
}
fh, err := os.Open(filepath.Join("/sys/kernel/btf", module))
if err != nil {
return nil, err
}
defer fh.Close()
return loadRawSpec(fh, internal.NativeEndian, base)
}
// findVMLinux scans multiple well-known paths for vmlinux kernel images.
func findVMLinux() (*os.File, error) {
if platform.IsWindows {
return nil, fmt.Errorf("find vmlinux: %w", internal.ErrNotSupportedOnOS)
}
release, err := linux.KernelRelease()
if err != nil {
return nil, err
}
// use same list of locations as libbpf
// https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122
locations := []string{
"/boot/vmlinux-%s",
"/lib/modules/%s/vmlinux-%[1]s",
"/lib/modules/%s/build/vmlinux",
"/usr/lib/modules/%s/kernel/vmlinux",
"/usr/lib/debug/boot/vmlinux-%s",
"/usr/lib/debug/boot/vmlinux-%s.debug",
"/usr/lib/debug/lib/modules/%s/vmlinux",
}
for _, loc := range locations {
file, err := os.Open(fmt.Sprintf(loc, release))
if errors.Is(err, os.ErrNotExist) {
continue
}
return file, err
}
return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported)
}