func (p *Program) Unpin() error {
	if err := internal.Unpin(p.pinnedPath); err != nil {
		return err
	}
	p.pinnedPath = ""
	return nil
}
Unpin removes the persisted state for the Program from the BPF virtual filesystem.

Failed calls to Unpin will not alter the state returned by IsPinned.

Unpinning an unpinned Program returns nil.
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)
}
func NewProgramFromFD(fd int) (*Program, error) {
	if fd < 0 {
		return nil, errors.New("invalid fd")
	}
	return newProgramFromFD(internal.NewFD(uint32(fd)))
}
func NewProgramFromID(id ProgramID) (*Program, error) {
	fd, err := bpfObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id))
	if err != nil {
		return nil, fmt.Errorf("get program by id: %w", err)
	}
	return newProgramFromFD(fd)
}
func newProgramFromFD(fd *internal.FD) (*Program, error) {
	info, err := newProgramInfoFromFd(fd)
	if err != nil {
		fd.Close()
		return nil, fmt.Errorf("discover program type: %w", err)
	}
	return &Program{"", fd, "", "", info.Type}, nil
}
func (p *Program) String() string {
	if p.name != "" {
		return fmt.Sprintf("%s(%s)#%v", p.typ, p.name, p.fd)
	}
	return fmt.Sprintf("%s(%v)", p.typ, p.fd)
}
func (p *Program) Type() ProgramType {
	return p.typ
}
func (p *Program) Info() (*ProgramInfo, error) {
	return newProgramInfoFromFd(p.fd)
}
func (p *Program) FD() int {
	fd, err := p.fd.Value()
	if err != nil {
		return -1
	}
	return int(fd)
}
func (p *Program) Clone() (*Program, error) {
	if p == nil {
		return nil, nil
	}
	dup, err := p.fd.Dup()
	if err != nil {
		return nil, fmt.Errorf("can't clone program: %w", err)
	}
	return &Program{p.VerifierLog, dup, p.name, "", p.typ}, nil
}
func (p *Program) Pin(fileName string) error {
	if err := internal.Pin(p.pinnedPath, fileName, p.fd); err != nil {
		return err
	}
	p.pinnedPath = fileName
	return nil
}