1package ebpf 2 3import ( 4 "errors" 5 "fmt" 6 "math" 7 8 "github.com/cilium/ebpf/asm" 9 "github.com/cilium/ebpf/internal" 10 "github.com/cilium/ebpf/internal/btf" 11) 12 13// CollectionOptions control loading a collection into the kernel. 14type CollectionOptions struct { 15 Programs ProgramOptions 16} 17 18// CollectionSpec describes a collection. 19type CollectionSpec struct { 20 Maps map[string]*MapSpec 21 Programs map[string]*ProgramSpec 22} 23 24// Copy returns a recursive copy of the spec. 25func (cs *CollectionSpec) Copy() *CollectionSpec { 26 if cs == nil { 27 return nil 28 } 29 30 cpy := CollectionSpec{ 31 Maps: make(map[string]*MapSpec, len(cs.Maps)), 32 Programs: make(map[string]*ProgramSpec, len(cs.Programs)), 33 } 34 35 for name, spec := range cs.Maps { 36 cpy.Maps[name] = spec.Copy() 37 } 38 39 for name, spec := range cs.Programs { 40 cpy.Programs[name] = spec.Copy() 41 } 42 43 return &cpy 44} 45 46// RewriteMaps replaces all references to specific maps. 47// 48// Use this function to use pre-existing maps instead of creating new ones 49// when calling NewCollection. Any named maps are removed from CollectionSpec.Maps. 50// 51// Returns an error if a named map isn't used in at least one program. 52func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error { 53 for symbol, m := range maps { 54 // have we seen a program that uses this symbol / map 55 seen := false 56 fd := m.FD() 57 for progName, progSpec := range cs.Programs { 58 err := progSpec.Instructions.RewriteMapPtr(symbol, fd) 59 60 switch { 61 case err == nil: 62 seen = true 63 64 case asm.IsUnreferencedSymbol(err): 65 // Not all programs need to use the map 66 67 default: 68 return fmt.Errorf("program %s: %w", progName, err) 69 } 70 } 71 72 if !seen { 73 return fmt.Errorf("map %s not referenced by any programs", symbol) 74 } 75 76 // Prevent NewCollection from creating rewritten maps 77 delete(cs.Maps, symbol) 78 } 79 80 return nil 81} 82 83// RewriteConstants replaces the value of multiple constants. 84// 85// The constant must be defined like so in the C program: 86// 87// static volatile const type foobar; 88// static volatile const type foobar = default; 89// 90// Replacement values must be of the same length as the C sizeof(type). 91// If necessary, they are marshalled according to the same rules as 92// map values. 93// 94// From Linux 5.5 the verifier will use constants to eliminate dead code. 95// 96// Returns an error if a constant doesn't exist. 97func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { 98 rodata := cs.Maps[".rodata"] 99 if rodata == nil { 100 return errors.New("missing .rodata section") 101 } 102 103 if rodata.BTF == nil { 104 return errors.New(".rodata section has no BTF") 105 } 106 107 if n := len(rodata.Contents); n != 1 { 108 return fmt.Errorf("expected one key in .rodata, found %d", n) 109 } 110 111 kv := rodata.Contents[0] 112 value, ok := kv.Value.([]byte) 113 if !ok { 114 return fmt.Errorf("first value in .rodata is %T not []byte", kv.Value) 115 } 116 117 buf := make([]byte, len(value)) 118 copy(buf, value) 119 120 err := patchValue(buf, btf.MapValue(rodata.BTF), consts) 121 if err != nil { 122 return err 123 } 124 125 rodata.Contents[0] = MapKV{kv.Key, buf} 126 return nil 127} 128 129// Collection is a collection of Programs and Maps associated 130// with their symbols 131type Collection struct { 132 Programs map[string]*Program 133 Maps map[string]*Map 134} 135 136// NewCollection creates a Collection from a specification. 137// 138// Only maps referenced by at least one of the programs are initialized. 139func NewCollection(spec *CollectionSpec) (*Collection, error) { 140 return NewCollectionWithOptions(spec, CollectionOptions{}) 141} 142 143// NewCollectionWithOptions creates a Collection from a specification. 144// 145// Only maps referenced by at least one of the programs are initialized. 146func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (coll *Collection, err error) { 147 var ( 148 maps = make(map[string]*Map) 149 progs = make(map[string]*Program) 150 btfs = make(map[*btf.Spec]*btf.Handle) 151 ) 152 153 defer func() { 154 for _, btf := range btfs { 155 btf.Close() 156 } 157 158 if err == nil { 159 return 160 } 161 162 for _, m := range maps { 163 m.Close() 164 } 165 166 for _, p := range progs { 167 p.Close() 168 } 169 }() 170 171 loadBTF := func(spec *btf.Spec) (*btf.Handle, error) { 172 if btfs[spec] != nil { 173 return btfs[spec], nil 174 } 175 176 handle, err := btf.NewHandle(spec) 177 if err != nil { 178 return nil, err 179 } 180 181 btfs[spec] = handle 182 return handle, nil 183 } 184 185 for mapName, mapSpec := range spec.Maps { 186 var handle *btf.Handle 187 if mapSpec.BTF != nil { 188 handle, err = loadBTF(btf.MapSpec(mapSpec.BTF)) 189 if err != nil && !errors.Is(err, btf.ErrNotSupported) { 190 return nil, err 191 } 192 } 193 194 m, err := newMapWithBTF(mapSpec, handle) 195 if err != nil { 196 return nil, fmt.Errorf("map %s: %w", mapName, err) 197 } 198 maps[mapName] = m 199 } 200 201 for progName, origProgSpec := range spec.Programs { 202 progSpec := origProgSpec.Copy() 203 204 // Rewrite any reference to a valid map. 205 for i := range progSpec.Instructions { 206 ins := &progSpec.Instructions[i] 207 208 if ins.OpCode != asm.LoadImmOp(asm.DWord) || ins.Reference == "" { 209 continue 210 } 211 212 if uint32(ins.Constant) != math.MaxUint32 { 213 // Don't overwrite maps already rewritten, users can 214 // rewrite programs in the spec themselves 215 continue 216 } 217 218 m := maps[ins.Reference] 219 if m == nil { 220 return nil, fmt.Errorf("program %s: missing map %s", progName, ins.Reference) 221 } 222 223 fd := m.FD() 224 if fd < 0 { 225 return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) 226 } 227 if err := ins.RewriteMapPtr(m.FD()); err != nil { 228 return nil, fmt.Errorf("progam %s: map %s: %w", progName, ins.Reference, err) 229 } 230 } 231 232 var handle *btf.Handle 233 if progSpec.BTF != nil { 234 handle, err = loadBTF(btf.ProgramSpec(progSpec.BTF)) 235 if err != nil && !errors.Is(err, btf.ErrNotSupported) { 236 return nil, err 237 } 238 } 239 240 prog, err := newProgramWithBTF(progSpec, handle, opts.Programs) 241 if err != nil { 242 return nil, fmt.Errorf("program %s: %w", progName, err) 243 } 244 progs[progName] = prog 245 } 246 247 return &Collection{ 248 progs, 249 maps, 250 }, nil 251} 252 253// LoadCollection parses an object file and converts it to a collection. 254func LoadCollection(file string) (*Collection, error) { 255 spec, err := LoadCollectionSpec(file) 256 if err != nil { 257 return nil, err 258 } 259 return NewCollection(spec) 260} 261 262// Close frees all maps and programs associated with the collection. 263// 264// The collection mustn't be used afterwards. 265func (coll *Collection) Close() { 266 for _, prog := range coll.Programs { 267 prog.Close() 268 } 269 for _, m := range coll.Maps { 270 m.Close() 271 } 272} 273 274// DetachMap removes the named map from the Collection. 275// 276// This means that a later call to Close() will not affect this map. 277// 278// Returns nil if no map of that name exists. 279func (coll *Collection) DetachMap(name string) *Map { 280 m := coll.Maps[name] 281 delete(coll.Maps, name) 282 return m 283} 284 285// DetachProgram removes the named program from the Collection. 286// 287// This means that a later call to Close() will not affect this program. 288// 289// Returns nil if no program of that name exists. 290func (coll *Collection) DetachProgram(name string) *Program { 291 p := coll.Programs[name] 292 delete(coll.Programs, name) 293 return p 294} 295