func (s *Spec) Datasec(name string) (*Map, error) {
	var datasec Datasec
	if err := s.FindType(name, &datasec); err != nil {
		return nil, fmt.Errorf("data section %s: can't get BTF: %w", name, err)
	}
	m := NewMap(s, &Void{}, &datasec)
	return &m, nil
}
Datasec returns the BTF required to create maps which represent data sections.
package btf
import (
	"bytes"
	"debug/elf"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"os"
	"reflect"
	"sync"
	"unsafe"
	"github.com/cilium/ebpf/internal"
	"github.com/cilium/ebpf/internal/unix"
)
const btfMagic = 0xeB9F
var (
	ErrNotSupported   = internal.ErrNotSupported
	ErrNotFound       = errors.New("not found")
	ErrNoExtendedInfo = errors.New("no extended info")
)
type Spec struct {
	rawTypes   []rawType
	strings    stringTable
	types      []Type
	namedTypes map[string][]namedType
	funcInfos  map[string]extInfo
	lineInfos  map[string]extInfo
	coreRelos  map[string]coreRelos
	byteOrder  binary.ByteOrder
}
type btfHeader struct {
	Magic   uint16
	Version uint8
	Flags   uint8
	HdrLen  uint32
	TypeOff   uint32
	TypeLen   uint32
	StringOff uint32
	StringLen uint32
}
func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
	file, err := internal.NewSafeELFFile(rd)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	btfSection, btfExtSection, sectionSizes, err := findBtfSections(file)
	if err != nil {
		return nil, err
	}
	if btfSection == nil {
		return nil, fmt.Errorf("btf: %w", ErrNotFound)
	}
	symbols, err := file.Symbols()
	if err != nil {
		return nil, fmt.Errorf("can't read symbols: %v", err)
	}
	variableOffsets := make(map[variable]uint32)
	for _, symbol := range symbols {
		if idx := symbol.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE {
			continue
		}
		if int(symbol.Section) >= len(file.Sections) {
			return nil, fmt.Errorf("symbol %s: invalid section %d", symbol.Name, symbol.Section)
		}
		secName := file.Sections[symbol.Section].Name
		if _, ok := sectionSizes[secName]; !ok {
			continue
		}
		if symbol.Value > math.MaxUint32 {
			return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name)
		}
		variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value)
	}
	spec, err := loadNakedSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets)
	if err != nil {
		return nil, err
	}
	if btfExtSection == nil {
		return spec, nil
	}
	spec.funcInfos, spec.lineInfos, spec.coreRelos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings)
	if err != nil {
		return nil, fmt.Errorf("can't read ext info: %w", err)
	}
	return spec, nil
}
func findBtfSections(file *internal.SafeELFFile) (*elf.Section, *elf.Section, map[string]uint32, error) {
	var (
		btfSection    *elf.Section
		btfExtSection *elf.Section
		sectionSizes  = make(map[string]uint32)
	)
	for _, sec := range file.Sections {
		switch sec.Name {
		case ".BTF":
			btfSection = sec
		case ".BTF.ext":
			btfExtSection = sec
		default:
			if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS {
				break
			}
			if sec.Size > math.MaxUint32 {
				return nil, nil, nil, fmt.Errorf("section %s exceeds maximum size", sec.Name)
			}
			sectionSizes[sec.Name] = uint32(sec.Size)
		}
	}
	return btfSection, btfExtSection, sectionSizes, nil
}
func loadSpecFromVmlinux(rd io.ReaderAt) (*Spec, error) {
	file, err := internal.NewSafeELFFile(rd)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	btfSection, _, _, err := findBtfSections(file)
	if err != nil {
		return nil, fmt.Errorf(".BTF ELF section: %s", err)
	}
	if btfSection == nil {
		return nil, fmt.Errorf("unable to find .BTF ELF section")
	}
	return loadNakedSpec(btfSection.Open(), file.ByteOrder, nil, nil)
}
func loadNakedSpec(btf io.ReadSeeker, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) {
	rawTypes, rawStrings, err := parseBTF(btf, bo)
	if err != nil {
		return nil, err
	}
	err = fixupDatasec(rawTypes, rawStrings, sectionSizes, variableOffsets)
	if err != nil {
		return nil, err
	}
	types, typesByName, err := inflateRawTypes(rawTypes, rawStrings)
	if err != nil {
		return nil, err
	}
	return &Spec{
		rawTypes:   rawTypes,
		namedTypes: typesByName,
		types:      types,
		strings:    rawStrings,
		byteOrder:  bo,
	}, nil
}
var kernelBTF struct {
	sync.Mutex
	*Spec
}
func LoadKernelSpec() (*Spec, error) {
	kernelBTF.Lock()
	defer kernelBTF.Unlock()
	if kernelBTF.Spec != nil {
		return kernelBTF.Spec, nil
	}
	var err error
	kernelBTF.Spec, err = loadKernelSpec()
	return kernelBTF.Spec, err
}
func loadKernelSpec() (*Spec, error) {
	release, err := unix.KernelRelease()
	if err != nil {
		return nil, fmt.Errorf("can't read kernel release number: %w", err)
	}
	fh, err := os.Open("/sys/kernel/btf/vmlinux")
	if err == nil {
		defer fh.Close()
		return loadNakedSpec(fh, internal.NativeEndian, nil, nil)
	}
	locations := []string{
		"/boot/vmlinux-%s",
		"/lib/modules/%s/vmlinux-%[1]s",
		"/lib/modules/%s/build/vmlinux",
		"/usr/lib/modules/%s/kernel/vmlinux",
		"/usr/lib/debug/boot/vmlinux-%s",
		"/usr/lib/debug/boot/vmlinux-%s.debug",
		"/usr/lib/debug/lib/modules/%s/vmlinux",
	}
	for _, loc := range locations {
		path := fmt.Sprintf(loc, release)
		fh, err := os.Open(path)
		if err != nil {
			continue
		}
		defer fh.Close()
		return loadSpecFromVmlinux(fh)
	}
	return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported)
}
func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) {
	rawBTF, err := ioutil.ReadAll(btf)
	if err != nil {
		return nil, nil, fmt.Errorf("can't read BTF: %v", err)
	}
	rd := bytes.NewReader(rawBTF)
	var header btfHeader
	if err := binary.Read(rd, bo, &header); err != nil {
		return nil, nil, fmt.Errorf("can't read header: %v", err)
	}
	if header.Magic != btfMagic {
		return nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic)
	}
	if header.Version != 1 {
		return nil, nil, fmt.Errorf("unexpected version %v", header.Version)
	}
	if header.Flags != 0 {
		return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags)
	}
	remainder := int64(header.HdrLen) - int64(binary.Size(&header))
	if remainder < 0 {
		return nil, nil, errors.New("header is too short")
	}
	if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil {
		return nil, nil, fmt.Errorf("header padding: %v", err)
	}
	if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil {
		return nil, nil, fmt.Errorf("can't seek to start of string section: %v", err)
	}
	rawStrings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen)))
	if err != nil {
		return nil, nil, fmt.Errorf("can't read type names: %w", err)
	}
	if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil {
		return nil, nil, fmt.Errorf("can't seek to start of type section: %v", err)
	}
	rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo)
	if err != nil {
		return nil, nil, fmt.Errorf("can't read types: %w", err)
	}
	return rawTypes, rawStrings, nil
}
type variable struct {
	section string
	name    string
}
func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) error {
	for i, rawType := range rawTypes {
		if rawType.Kind() != kindDatasec {
			continue
		}
		name, err := rawStrings.Lookup(rawType.NameOff)
		if err != nil {
			return err
		}
		if name == ".kconfig" || name == ".ksyms" {
			return fmt.Errorf("reference to %s: %w", name, ErrNotSupported)
		}
		if rawTypes[i].SizeType != 0 {
			continue
		}
		size, ok := sectionSizes[name]
		if !ok {
			return fmt.Errorf("data section %s: missing size", name)
		}
		rawTypes[i].SizeType = size
		secinfos := rawType.data.([]btfVarSecinfo)
		for j, secInfo := range secinfos {
			id := int(secInfo.Type - 1)
			if id >= len(rawTypes) {
				return fmt.Errorf("data section %s: invalid type id %d for variable %d", name, id, j)
			}
			varName, err := rawStrings.Lookup(rawTypes[id].NameOff)
			if err != nil {
				return fmt.Errorf("data section %s: can't get name for type %d: %w", name, id, err)
			}
			offset, ok := variableOffsets[variable{name, varName}]
			if !ok {
				return fmt.Errorf("data section %s: missing offset for variable %s", name, varName)
			}
			secinfos[j].Offset = offset
		}
	}
	return nil
}
type marshalOpts struct {
	ByteOrder        binary.ByteOrder
	StripFuncLinkage bool
}
func (s *Spec) marshal(opts marshalOpts) ([]byte, error) {
	var (
		buf       bytes.Buffer
		header    = new(btfHeader)
		headerLen = binary.Size(header)
	)
	_, _ = buf.Write(make([]byte, headerLen))
	for _, raw := range s.rawTypes {
		switch {
		case opts.StripFuncLinkage && raw.Kind() == kindFunc:
			raw.SetLinkage(StaticFunc)
		}
		if err := raw.Marshal(&buf, opts.ByteOrder); err != nil {
			return nil, fmt.Errorf("can't marshal BTF: %w", err)
		}
	}
	typeLen := uint32(buf.Len() - headerLen)
	_, _ = buf.Write(s.strings)
	header = &btfHeader{
		Magic:     btfMagic,
		Version:   1,
		Flags:     0,
		HdrLen:    uint32(headerLen),
		TypeOff:   0,
		TypeLen:   typeLen,
		StringOff: typeLen,
		StringLen: uint32(len(s.strings)),
	}
	raw := buf.Bytes()
	err := binary.Write(sliceWriter(raw[:headerLen]), opts.ByteOrder, header)
	if err != nil {
		return nil, fmt.Errorf("can't write header: %v", err)
	}
	return raw, nil
}
type sliceWriter []byte
func (sw sliceWriter) Write(p []byte) (int, error) {
	if len(p) != len(sw) {
		return 0, errors.New("size doesn't match")
	}
	return copy(sw, p), nil
}
func (s *Spec) Program(name string, length uint64) (*Program, error) {
	if length == 0 {
		return nil, errors.New("length musn't be zero")
	}
	if s.funcInfos == nil && s.lineInfos == nil && s.coreRelos == nil {
		return nil, fmt.Errorf("BTF for section %s: %w", name, ErrNoExtendedInfo)
	}
	funcInfos, funcOK := s.funcInfos[name]
	lineInfos, lineOK := s.lineInfos[name]
	relos, coreOK := s.coreRelos[name]
	if !funcOK && !lineOK && !coreOK {
		return nil, fmt.Errorf("no extended BTF info for section %s", name)
	}
	return &Program{s, length, funcInfos, lineInfos, relos}, nil
}