Files
runc/vendor/github.com/cilium/ebpf/variable.go
dependabot[bot] e809db842f build(deps): bump github.com/cilium/ebpf from 0.16.0 to 0.17.0
Bumps [github.com/cilium/ebpf](https://github.com/cilium/ebpf) from 0.16.0 to 0.17.0.
- [Release notes](https://github.com/cilium/ebpf/releases)
- [Commits](https://github.com/cilium/ebpf/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: github.com/cilium/ebpf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-20 00:07:03 +00:00

231 lines
6.1 KiB
Go

package ebpf
import (
"fmt"
"io"
"github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/internal/sysenc"
)
// VariableSpec is a convenience wrapper for modifying global variables of a
// CollectionSpec before loading it into the kernel.
//
// All operations on a VariableSpec's underlying MapSpec are performed in the
// host's native endianness.
type VariableSpec struct {
name string
offset uint64
size uint64
m *MapSpec
t *btf.Var
}
// Set sets the value of the VariableSpec to the provided input using the host's
// native endianness.
func (s *VariableSpec) Set(in any) error {
buf, err := sysenc.Marshal(in, int(s.size))
if err != nil {
return fmt.Errorf("marshaling value %s: %w", s.name, err)
}
b, _, err := s.m.dataSection()
if err != nil {
return fmt.Errorf("getting data section of map %s: %w", s.m.Name, err)
}
if int(s.offset+s.size) > len(b) {
return fmt.Errorf("offset %d(+%d) for variable %s is out of bounds", s.offset, s.size, s.name)
}
// MapSpec.Copy() performs a shallow copy. Fully copy the byte slice
// to avoid any changes affecting other copies of the MapSpec.
cpy := make([]byte, len(b))
copy(cpy, b)
buf.CopyTo(cpy[s.offset : s.offset+s.size])
s.m.Contents[0] = MapKV{Key: uint32(0), Value: cpy}
return nil
}
// Get writes the value of the VariableSpec to the provided output using the
// host's native endianness.
func (s *VariableSpec) Get(out any) error {
b, _, err := s.m.dataSection()
if err != nil {
return fmt.Errorf("getting data section of map %s: %w", s.m.Name, err)
}
if int(s.offset+s.size) > len(b) {
return fmt.Errorf("offset %d(+%d) for variable %s is out of bounds", s.offset, s.size, s.name)
}
if err := sysenc.Unmarshal(out, b[s.offset:s.offset+s.size]); err != nil {
return fmt.Errorf("unmarshaling value: %w", err)
}
return nil
}
// Size returns the size of the variable in bytes.
func (s *VariableSpec) Size() uint64 {
return s.size
}
// MapName returns the name of the underlying MapSpec.
func (s *VariableSpec) MapName() string {
return s.m.Name
}
// Offset returns the offset of the variable in the underlying MapSpec.
func (s *VariableSpec) Offset() uint64 {
return s.offset
}
// Constant returns true if the VariableSpec represents a variable that is
// read-only from the perspective of the BPF program.
func (s *VariableSpec) Constant() bool {
return s.m.readOnly()
}
// Type returns the [btf.Var] representing the variable in its data section.
// This is useful for inspecting the variable's decl tags and the type
// information of the inner type.
//
// Returns nil if the original ELF object did not contain BTF information.
func (s *VariableSpec) Type() *btf.Var {
return s.t
}
func (s *VariableSpec) String() string {
return fmt.Sprintf("%s (type=%v, map=%s, offset=%d, size=%d)", s.name, s.t, s.m.Name, s.offset, s.size)
}
// copy returns a new VariableSpec with the same values as the original,
// but with a different underlying MapSpec. This is useful when copying a
// CollectionSpec. Returns nil if a MapSpec with the same name is not found.
func (s *VariableSpec) copy(cpy *CollectionSpec) *VariableSpec {
out := &VariableSpec{
name: s.name,
offset: s.offset,
size: s.size,
}
if s.t != nil {
out.t = btf.Copy(s.t).(*btf.Var)
}
// Attempt to find a MapSpec with the same name in the copied CollectionSpec.
for _, m := range cpy.Maps {
if m.Name == s.m.Name {
out.m = m
return out
}
}
return nil
}
// Variable is a convenience wrapper for modifying global variables of a
// Collection after loading it into the kernel. Operations on a Variable are
// performed using direct memory access, bypassing the BPF map syscall API.
//
// On kernels older than 5.5, most interactions with Variable return
// [ErrNotSupported].
type Variable struct {
name string
offset uint64
size uint64
t *btf.Var
mm *Memory
}
func newVariable(name string, offset, size uint64, t *btf.Var, mm *Memory) (*Variable, error) {
if mm != nil {
if int(offset+size) > mm.Size() {
return nil, fmt.Errorf("offset %d(+%d) is out of bounds", offset, size)
}
}
return &Variable{
name: name,
offset: offset,
size: size,
t: t,
mm: mm,
}, nil
}
// Size returns the size of the variable.
func (v *Variable) Size() uint64 {
return v.size
}
// ReadOnly returns true if the Variable represents a variable that is read-only
// after loading the Collection into the kernel.
//
// On systems without BPF_F_MMAPABLE support, ReadOnly always returns true.
func (v *Variable) ReadOnly() bool {
if v.mm == nil {
return true
}
return v.mm.ReadOnly()
}
// Type returns the [btf.Var] representing the variable in its data section.
// This is useful for inspecting the variable's decl tags and the type
// information of the inner type.
//
// Returns nil if the original ELF object did not contain BTF information.
func (v *Variable) Type() *btf.Var {
return v.t
}
func (v *Variable) String() string {
return fmt.Sprintf("%s (type=%v)", v.name, v.t)
}
// Set the value of the Variable to the provided input. The input must marshal
// to the same length as the size of the Variable.
func (v *Variable) Set(in any) error {
if v.mm == nil {
return fmt.Errorf("variable %s: direct access requires Linux 5.5 or later: %w", v.name, ErrNotSupported)
}
if v.ReadOnly() {
return fmt.Errorf("variable %s: %w", v.name, ErrReadOnly)
}
buf, err := sysenc.Marshal(in, int(v.size))
if err != nil {
return fmt.Errorf("marshaling value %s: %w", v.name, err)
}
if _, err := v.mm.WriteAt(buf.Bytes(), int64(v.offset)); err != nil {
return fmt.Errorf("writing value to %s: %w", v.name, err)
}
return nil
}
// Get writes the value of the Variable to the provided output. The output must
// be a pointer to a value whose size matches the Variable.
func (v *Variable) Get(out any) error {
if v.mm == nil {
return fmt.Errorf("variable %s: direct access requires Linux 5.5 or later: %w", v.name, ErrNotSupported)
}
if !v.mm.bounds(v.offset, v.size) {
return fmt.Errorf("variable %s: access out of bounds: %w", v.name, io.EOF)
}
if err := sysenc.Unmarshal(out, v.mm.b[v.offset:v.offset+v.size]); err != nil {
return fmt.Errorf("unmarshaling value %s: %w", v.name, err)
}
return nil
}