...

Source file src/github.com/cilium/ebpf/marshalers.go

Documentation: github.com/cilium/ebpf

     1  package ebpf
     2  
     3  import (
     4  	"bytes"
     5  	"encoding"
     6  	"encoding/binary"
     7  	"errors"
     8  	"fmt"
     9  	"reflect"
    10  	"runtime"
    11  	"sync"
    12  	"unsafe"
    13  
    14  	"github.com/cilium/ebpf/internal"
    15  	"github.com/cilium/ebpf/internal/sys"
    16  )
    17  
    18  // marshalPtr converts an arbitrary value into a pointer suitable
    19  // to be passed to the kernel.
    20  //
    21  // As an optimization, it returns the original value if it is an
    22  // unsafe.Pointer.
    23  func marshalPtr(data interface{}, length int) (sys.Pointer, error) {
    24  	if ptr, ok := data.(unsafe.Pointer); ok {
    25  		return sys.NewPointer(ptr), nil
    26  	}
    27  
    28  	buf, err := marshalBytes(data, length)
    29  	if err != nil {
    30  		return sys.Pointer{}, err
    31  	}
    32  
    33  	return sys.NewSlicePointer(buf), nil
    34  }
    35  
    36  // marshalBytes converts an arbitrary value into a byte buffer.
    37  //
    38  // Prefer using Map.marshalKey and Map.marshalValue if possible, since
    39  // those have special cases that allow more types to be encoded.
    40  //
    41  // Returns an error if the given value isn't representable in exactly
    42  // length bytes.
    43  func marshalBytes(data interface{}, length int) (buf []byte, err error) {
    44  	if data == nil {
    45  		return nil, errors.New("can't marshal a nil value")
    46  	}
    47  
    48  	switch value := data.(type) {
    49  	case encoding.BinaryMarshaler:
    50  		buf, err = value.MarshalBinary()
    51  	case string:
    52  		buf = []byte(value)
    53  	case []byte:
    54  		buf = value
    55  	case unsafe.Pointer:
    56  		err = errors.New("can't marshal from unsafe.Pointer")
    57  	case Map, *Map, Program, *Program:
    58  		err = fmt.Errorf("can't marshal %T", value)
    59  	default:
    60  		var wr bytes.Buffer
    61  		err = binary.Write(&wr, internal.NativeEndian, value)
    62  		if err != nil {
    63  			err = fmt.Errorf("encoding %T: %v", value, err)
    64  		}
    65  		buf = wr.Bytes()
    66  	}
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	if len(buf) != length {
    72  		return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length)
    73  	}
    74  	return buf, nil
    75  }
    76  
    77  func makeBuffer(dst interface{}, length int) (sys.Pointer, []byte) {
    78  	if ptr, ok := dst.(unsafe.Pointer); ok {
    79  		return sys.NewPointer(ptr), nil
    80  	}
    81  
    82  	buf := make([]byte, length)
    83  	return sys.NewSlicePointer(buf), buf
    84  }
    85  
    86  var bytesReaderPool = sync.Pool{
    87  	New: func() interface{} {
    88  		return new(bytes.Reader)
    89  	},
    90  }
    91  
    92  // unmarshalBytes converts a byte buffer into an arbitrary value.
    93  //
    94  // Prefer using Map.unmarshalKey and Map.unmarshalValue if possible, since
    95  // those have special cases that allow more types to be encoded.
    96  //
    97  // The common int32 and int64 types are directly handled to avoid
    98  // unnecessary heap allocations as happening in the default case.
    99  func unmarshalBytes(data interface{}, buf []byte) error {
   100  	switch value := data.(type) {
   101  	case unsafe.Pointer:
   102  		dst := unsafe.Slice((*byte)(value), len(buf))
   103  		copy(dst, buf)
   104  		runtime.KeepAlive(value)
   105  		return nil
   106  	case Map, *Map, Program, *Program:
   107  		return fmt.Errorf("can't unmarshal into %T", value)
   108  	case encoding.BinaryUnmarshaler:
   109  		return value.UnmarshalBinary(buf)
   110  	case *string:
   111  		*value = string(buf)
   112  		return nil
   113  	case *[]byte:
   114  		*value = buf
   115  		return nil
   116  	case *int32:
   117  		if len(buf) < 4 {
   118  			return errors.New("int32 requires 4 bytes")
   119  		}
   120  		*value = int32(internal.NativeEndian.Uint32(buf))
   121  		return nil
   122  	case *uint32:
   123  		if len(buf) < 4 {
   124  			return errors.New("uint32 requires 4 bytes")
   125  		}
   126  		*value = internal.NativeEndian.Uint32(buf)
   127  		return nil
   128  	case *int64:
   129  		if len(buf) < 8 {
   130  			return errors.New("int64 requires 8 bytes")
   131  		}
   132  		*value = int64(internal.NativeEndian.Uint64(buf))
   133  		return nil
   134  	case *uint64:
   135  		if len(buf) < 8 {
   136  			return errors.New("uint64 requires 8 bytes")
   137  		}
   138  		*value = internal.NativeEndian.Uint64(buf)
   139  		return nil
   140  	case string:
   141  		return errors.New("require pointer to string")
   142  	case []byte:
   143  		return errors.New("require pointer to []byte")
   144  	default:
   145  		rd := bytesReaderPool.Get().(*bytes.Reader)
   146  		rd.Reset(buf)
   147  		defer bytesReaderPool.Put(rd)
   148  		if err := binary.Read(rd, internal.NativeEndian, value); err != nil {
   149  			return fmt.Errorf("decoding %T: %v", value, err)
   150  		}
   151  		return nil
   152  	}
   153  }
   154  
   155  // marshalPerCPUValue encodes a slice containing one value per
   156  // possible CPU into a buffer of bytes.
   157  //
   158  // Values are initialized to zero if the slice has less elements than CPUs.
   159  //
   160  // slice must have a type like []elementType.
   161  func marshalPerCPUValue(slice interface{}, elemLength int) (sys.Pointer, error) {
   162  	sliceType := reflect.TypeOf(slice)
   163  	if sliceType.Kind() != reflect.Slice {
   164  		return sys.Pointer{}, errors.New("per-CPU value requires slice")
   165  	}
   166  
   167  	possibleCPUs, err := internal.PossibleCPUs()
   168  	if err != nil {
   169  		return sys.Pointer{}, err
   170  	}
   171  
   172  	sliceValue := reflect.ValueOf(slice)
   173  	sliceLen := sliceValue.Len()
   174  	if sliceLen > possibleCPUs {
   175  		return sys.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs")
   176  	}
   177  
   178  	alignedElemLength := internal.Align(elemLength, 8)
   179  	buf := make([]byte, alignedElemLength*possibleCPUs)
   180  
   181  	for i := 0; i < sliceLen; i++ {
   182  		elem := sliceValue.Index(i).Interface()
   183  		elemBytes, err := marshalBytes(elem, elemLength)
   184  		if err != nil {
   185  			return sys.Pointer{}, err
   186  		}
   187  
   188  		offset := i * alignedElemLength
   189  		copy(buf[offset:offset+elemLength], elemBytes)
   190  	}
   191  
   192  	return sys.NewSlicePointer(buf), nil
   193  }
   194  
   195  // unmarshalPerCPUValue decodes a buffer into a slice containing one value per
   196  // possible CPU.
   197  //
   198  // valueOut must have a type like *[]elementType
   199  func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error {
   200  	slicePtrType := reflect.TypeOf(slicePtr)
   201  	if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {
   202  		return fmt.Errorf("per-cpu value requires pointer to slice")
   203  	}
   204  
   205  	possibleCPUs, err := internal.PossibleCPUs()
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	sliceType := slicePtrType.Elem()
   211  	slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs)
   212  
   213  	sliceElemType := sliceType.Elem()
   214  	sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr
   215  	if sliceElemIsPointer {
   216  		sliceElemType = sliceElemType.Elem()
   217  	}
   218  
   219  	step := len(buf) / possibleCPUs
   220  	if step < elemLength {
   221  		return fmt.Errorf("per-cpu element length is larger than available data")
   222  	}
   223  	for i := 0; i < possibleCPUs; i++ {
   224  		var elem interface{}
   225  		if sliceElemIsPointer {
   226  			newElem := reflect.New(sliceElemType)
   227  			slice.Index(i).Set(newElem)
   228  			elem = newElem.Interface()
   229  		} else {
   230  			elem = slice.Index(i).Addr().Interface()
   231  		}
   232  
   233  		// Make a copy, since unmarshal can hold on to itemBytes
   234  		elemBytes := make([]byte, elemLength)
   235  		copy(elemBytes, buf[:elemLength])
   236  
   237  		err := unmarshalBytes(elem, elemBytes)
   238  		if err != nil {
   239  			return fmt.Errorf("cpu %d: %w", i, err)
   240  		}
   241  
   242  		buf = buf[step:]
   243  	}
   244  
   245  	reflect.ValueOf(slicePtr).Elem().Set(slice)
   246  	return nil
   247  }
   248  

View as plain text