1 package ebpf
2
3 import (
4 "errors"
5 "fmt"
6 "sync"
7
8 "github.com/cilium/ebpf/asm"
9 "github.com/cilium/ebpf/btf"
10 )
11
12
13
14
15
16 func splitSymbols(insns asm.Instructions) (map[string]asm.Instructions, error) {
17 if len(insns) == 0 {
18 return nil, errors.New("insns is empty")
19 }
20
21 if insns[0].Symbol() == "" {
22 return nil, errors.New("insns must start with a Symbol")
23 }
24
25 var name string
26 progs := make(map[string]asm.Instructions)
27 for _, ins := range insns {
28 if sym := ins.Symbol(); sym != "" {
29 if progs[sym] != nil {
30 return nil, fmt.Errorf("insns contains duplicate Symbol %s", sym)
31 }
32 name = sym
33 }
34
35 progs[name] = append(progs[name], ins)
36 }
37
38 return progs, nil
39 }
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 func hasFunctionReferences(insns asm.Instructions) bool {
58 for _, i := range insns {
59 if i.IsFunctionReference() {
60 return true
61 }
62 }
63 return false
64 }
65
66
67
68
69
70 func applyRelocations(insns asm.Instructions, local, target *btf.Spec) error {
71 var relos []*btf.CORERelocation
72 var reloInsns []*asm.Instruction
73 iter := insns.Iterate()
74 for iter.Next() {
75 if relo := btf.CORERelocationMetadata(iter.Ins); relo != nil {
76 relos = append(relos, relo)
77 reloInsns = append(reloInsns, iter.Ins)
78 }
79 }
80
81 if len(relos) == 0 {
82 return nil
83 }
84
85 target, err := maybeLoadKernelBTF(target)
86 if err != nil {
87 return err
88 }
89
90 fixups, err := btf.CORERelocate(local, target, relos)
91 if err != nil {
92 return err
93 }
94
95 for i, fixup := range fixups {
96 if err := fixup.Apply(reloInsns[i]); err != nil {
97 return fmt.Errorf("apply fixup %s: %w", &fixup, err)
98 }
99 }
100
101 return nil
102 }
103
104
105
106
107 func flattenPrograms(progs map[string]*ProgramSpec, names []string) {
108
109 refs := make(map[*ProgramSpec][]string)
110 for _, prog := range progs {
111 refs[prog] = prog.Instructions.FunctionReferences()
112 }
113
114
115
116 flattened := make([]asm.Instructions, 0, len(names))
117 for _, name := range names {
118 flattened = append(flattened, flattenInstructions(name, progs, refs))
119 }
120
121
122 for i, name := range names {
123 progs[name].Instructions = flattened[i]
124 }
125 }
126
127
128
129
130
131
132
133
134 func flattenInstructions(name string, progs map[string]*ProgramSpec, refs map[*ProgramSpec][]string) asm.Instructions {
135 prog := progs[name]
136
137 insns := make(asm.Instructions, len(prog.Instructions))
138 copy(insns, prog.Instructions)
139
140
141 pending := make([]string, len(refs[prog]))
142 copy(pending, refs[prog])
143
144
145 linked := make(map[string]bool)
146
147
148
149 for len(pending) > 0 {
150 var ref string
151 ref, pending = pending[0], pending[1:]
152
153 if linked[ref] {
154
155 continue
156 }
157
158 progRef := progs[ref]
159 if progRef == nil {
160
161
162 continue
163 }
164
165 insns = append(insns, progRef.Instructions...)
166 linked[ref] = true
167
168
169 pending = append(pending, refs[progRef]...)
170 }
171
172 return insns
173 }
174
175
176
177
178 func fixupAndValidate(insns asm.Instructions) error {
179 iter := insns.Iterate()
180 for iter.Next() {
181 ins := iter.Ins
182
183
184 if ins.IsLoadFromMap() && ins.Reference() != "" && ins.Map() == nil {
185 return fmt.Errorf("instruction %d: map %s: %w", iter.Index, ins.Reference(), asm.ErrUnsatisfiedMapReference)
186 }
187
188 fixupProbeReadKernel(ins)
189 }
190
191 return nil
192 }
193
194
195
196 func fixupProbeReadKernel(ins *asm.Instruction) {
197 if !ins.IsBuiltinCall() {
198 return
199 }
200
201
202 if haveProbeReadKernel() == nil {
203 return
204 }
205
206 switch asm.BuiltinFunc(ins.Constant) {
207 case asm.FnProbeReadKernel, asm.FnProbeReadUser:
208 ins.Constant = int64(asm.FnProbeRead)
209 case asm.FnProbeReadKernelStr, asm.FnProbeReadUserStr:
210 ins.Constant = int64(asm.FnProbeReadStr)
211 }
212 }
213
214 var kernelBTF struct {
215 sync.Mutex
216 spec *btf.Spec
217 }
218
219
220
221
222
223 func maybeLoadKernelBTF(spec *btf.Spec) (*btf.Spec, error) {
224 if spec != nil {
225 return spec, nil
226 }
227
228 kernelBTF.Lock()
229 defer kernelBTF.Unlock()
230
231 if kernelBTF.spec != nil {
232 return kernelBTF.spec, nil
233 }
234
235 var err error
236 kernelBTF.spec, err = btf.LoadKernelSpec()
237 return kernelBTF.spec, err
238 }
239
View as plain text