func NewProgramFromFD(fd int) (*Program, error) {
	if fd < 0 {
		return nil, errors.New("invalid fd")
	}
	return newProgramFromFD(internal.NewFD(uint32(fd)))
}
NewProgramFromFD creates a program from a raw fd.

You should not use fd after calling this function.

Requires at least Linux 4.10.
package ebpf
import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"math"
	"path/filepath"
	"strings"
	"time"
	"github.com/cilium/ebpf/asm"
	"github.com/cilium/ebpf/internal"
	"github.com/cilium/ebpf/internal/btf"
	"github.com/cilium/ebpf/internal/unix"
)
var ErrNotSupported = internal.ErrNotSupported
var errUnsatisfiedReference = errors.New("unsatisfied reference")
type ProgramID uint32
const (
	outputPad = 256 + 2
)
const DefaultVerifierLogSize = 64 * 1024
type ProgramOptions struct {
	LogLevel uint32
	LogSize int
	TargetBTF io.ReaderAt
}
type ProgramSpec struct {
	Name string
	Type       ProgramType
	AttachType AttachType
	AttachTo     string
	Instructions asm.Instructions
	Flags uint32
	License string
	KernelVersion uint32
	BTF *btf.Program
	ByteOrder binary.ByteOrder
}
func (ps *ProgramSpec) Copy() *ProgramSpec {
	if ps == nil {
		return nil
	}
	cpy := *ps
	cpy.Instructions = make(asm.Instructions, len(ps.Instructions))
	copy(cpy.Instructions, ps.Instructions)
	return &cpy
}
func (ps *ProgramSpec) Tag() (string, error) {
	return ps.Instructions.Tag(internal.NativeEndian)
}
type Program struct {
	VerifierLog string
	fd         *internal.FD
	name       string
	pinnedPath string
	typ        ProgramType
}
func NewProgram(spec *ProgramSpec) (*Program, error) {
	return NewProgramWithOptions(spec, ProgramOptions{})
}
func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) {
	handles := newHandleCache()
	defer handles.close()
	prog, err := newProgramWithOptions(spec, opts, handles)
	if errors.Is(err, errUnsatisfiedReference) {
		return nil, fmt.Errorf("cannot load program without loading its whole collection: %w", err)
	}
	return prog, err
}
func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *handleCache) (*Program, error) {
	if len(spec.Instructions) == 0 {
		return nil, errors.New("Instructions cannot be empty")
	}
	if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian {
		return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian)
	}
	kv := spec.KernelVersion
	if spec.Type == Kprobe && (kv == 0 || kv == internal.MagicKernelVersion) {
		v, err := internal.KernelVersion()
		if err != nil {
			return nil, fmt.Errorf("detecting kernel version: %w", err)
		}
		kv = v.Kernel()
	}
	attr := &bpfProgLoadAttr{
		progType:           spec.Type,
		progFlags:          spec.Flags,
		expectedAttachType: spec.AttachType,
		license:            internal.NewStringPointer(spec.License),
		kernelVersion:      kv,
	}
	if haveObjName() == nil {
		attr.progName = internal.NewBPFObjName(spec.Name)
	}
	var err error
	var targetBTF *btf.Spec
	if opts.TargetBTF != nil {
		targetBTF, err = handles.btfSpec(opts.TargetBTF)
		if err != nil {
			return nil, fmt.Errorf("load target BTF: %w", err)
		}
	}
	var btfDisabled bool
	var core btf.COREFixups
	if spec.BTF != nil {
		core, err = btf.ProgramFixups(spec.BTF, targetBTF)
		if err != nil {
			return nil, fmt.Errorf("CO-RE relocations: %w", err)
		}
		handle, err := handles.btfHandle(btf.ProgramSpec(spec.BTF))
		btfDisabled = errors.Is(err, btf.ErrNotSupported)
		if err != nil && !btfDisabled {
			return nil, fmt.Errorf("load BTF: %w", err)
		}
		if handle != nil {
			attr.progBTFFd = uint32(handle.FD())
			recSize, bytes, err := btf.ProgramLineInfos(spec.BTF)
			if err != nil {
				return nil, fmt.Errorf("get BTF line infos: %w", err)
			}
			attr.lineInfoRecSize = recSize
			attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
			attr.lineInfo = internal.NewSlicePointer(bytes)
			recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF)
			if err != nil {
				return nil, fmt.Errorf("get BTF function infos: %w", err)
			}
			attr.funcInfoRecSize = recSize
			attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
			attr.funcInfo = internal.NewSlicePointer(bytes)
		}
	}
	insns, err := core.Apply(spec.Instructions)
	if err != nil {
		return nil, fmt.Errorf("CO-RE fixup: %w", err)
	}
	if err := fixupJumpsAndCalls(insns); err != nil {
		return nil, err
	}
	buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize))
	err = insns.Marshal(buf, internal.NativeEndian)
	if err != nil {
		return nil, err
	}
	bytecode := buf.Bytes()
	attr.instructions = internal.NewSlicePointer(bytecode)
	attr.insCount = uint32(len(bytecode) / asm.InstructionSize)
	if spec.AttachTo != "" {
		target, err := resolveBTFType(targetBTF, spec.AttachTo, spec.Type, spec.AttachType)
		if err != nil {
			return nil, err
		}
		if target != nil {
			attr.attachBTFID = target.ID()
		}
	}
	logSize := DefaultVerifierLogSize
	if opts.LogSize > 0 {
		logSize = opts.LogSize
	}
	var logBuf []byte
	if opts.LogLevel > 0 {
		logBuf = make([]byte, logSize)
		attr.logLevel = opts.LogLevel
		attr.logSize = uint32(len(logBuf))
		attr.logBuf = internal.NewSlicePointer(logBuf)
	}
	fd, err := bpfProgLoad(attr)
	if err == nil {
		return &Program{internal.CString(logBuf), fd, spec.Name, "", spec.Type}, nil
	}
	logErr := err
	if opts.LogLevel == 0 && opts.LogSize >= 0 {
		logBuf = make([]byte, logSize)
		attr.logLevel = 1
		attr.logSize = uint32(len(logBuf))
		attr.logBuf = internal.NewSlicePointer(logBuf)
		_, logErr = bpfProgLoad(attr)
	}
	if errors.Is(logErr, unix.EPERM) && logBuf[0] == 0 {
		return nil, fmt.Errorf("load program: RLIMIT_MEMLOCK may be too low: %w", logErr)
	}
	err = internal.ErrorWithLog(err, logBuf, logErr)
	if btfDisabled {
		return nil, fmt.Errorf("load program without BTF: %w", err)
	}
	return nil, fmt.Errorf("load program: %w", err)
}