marshalers.go (6490B)
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 }