mirror of
https://github.com/opencontainers/runc.git
synced 2025-11-02 21:55:03 +08:00
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>
231 lines
6.1 KiB
Go
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
|
|
}
|