github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/elf_reader.go (about)

     1  package ebpf
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"debug/elf"
     7  	"encoding/binary"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"math"
    12  	"os"
    13  	"strings"
    14  
    15  	"github.com/cilium/ebpf/asm"
    16  	"github.com/cilium/ebpf/btf"
    17  	"github.com/cilium/ebpf/internal"
    18  	"github.com/cilium/ebpf/internal/sys"
    19  	"github.com/cilium/ebpf/internal/unix"
    20  )
    21  
    22  type kconfigMetaKey struct{}
    23  
    24  type kconfigMeta struct {
    25  	Map    *MapSpec
    26  	Offset uint32
    27  }
    28  
    29  type kfuncMetaKey struct{}
    30  
    31  type kfuncMeta struct {
    32  	Binding elf.SymBind
    33  	Func    *btf.Func
    34  }
    35  
    36  // elfCode is a convenience to reduce the amount of arguments that have to
    37  // be passed around explicitly. You should treat its contents as immutable.
    38  type elfCode struct {
    39  	*internal.SafeELFFile
    40  	sections map[elf.SectionIndex]*elfSection
    41  	license  string
    42  	version  uint32
    43  	btf      *btf.Spec
    44  	extInfo  *btf.ExtInfos
    45  	maps     map[string]*MapSpec
    46  	kfuncs   map[string]*btf.Func
    47  	kconfig  *MapSpec
    48  }
    49  
    50  // LoadCollectionSpec parses an ELF file into a CollectionSpec.
    51  func LoadCollectionSpec(file string) (*CollectionSpec, error) {
    52  	f, err := os.Open(file)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	defer f.Close()
    57  
    58  	spec, err := LoadCollectionSpecFromReader(f)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("file %s: %w", file, err)
    61  	}
    62  	return spec, nil
    63  }
    64  
    65  // LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec.
    66  func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
    67  	f, err := internal.NewSafeELFFile(rd)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	// Checks if the ELF file is for BPF data.
    73  	// Old LLVM versions set e_machine to EM_NONE.
    74  	if f.File.Machine != unix.EM_NONE && f.File.Machine != elf.EM_BPF {
    75  		return nil, fmt.Errorf("unexpected machine type for BPF ELF: %s", f.File.Machine)
    76  	}
    77  
    78  	var (
    79  		licenseSection *elf.Section
    80  		versionSection *elf.Section
    81  		sections       = make(map[elf.SectionIndex]*elfSection)
    82  		relSections    = make(map[elf.SectionIndex]*elf.Section)
    83  	)
    84  
    85  	// This is the target of relocations generated by inline assembly.
    86  	sections[elf.SHN_UNDEF] = newElfSection(new(elf.Section), undefSection)
    87  
    88  	// Collect all the sections we're interested in. This includes relocations
    89  	// which we parse later.
    90  	//
    91  	// Keep the documentation at docs/ebpf/loading/elf-sections.md up-to-date.
    92  	for i, sec := range f.Sections {
    93  		idx := elf.SectionIndex(i)
    94  
    95  		switch {
    96  		case strings.HasPrefix(sec.Name, "license"):
    97  			licenseSection = sec
    98  		case strings.HasPrefix(sec.Name, "version"):
    99  			versionSection = sec
   100  		case strings.HasPrefix(sec.Name, "maps"):
   101  			sections[idx] = newElfSection(sec, mapSection)
   102  		case sec.Name == ".maps":
   103  			sections[idx] = newElfSection(sec, btfMapSection)
   104  		case sec.Name == ".bss" || sec.Name == ".data" || strings.HasPrefix(sec.Name, ".rodata"):
   105  			sections[idx] = newElfSection(sec, dataSection)
   106  		case sec.Type == elf.SHT_REL:
   107  			// Store relocations under the section index of the target
   108  			relSections[elf.SectionIndex(sec.Info)] = sec
   109  		case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0:
   110  			sections[idx] = newElfSection(sec, programSection)
   111  		}
   112  	}
   113  
   114  	license, err := loadLicense(licenseSection)
   115  	if err != nil {
   116  		return nil, fmt.Errorf("load license: %w", err)
   117  	}
   118  
   119  	version, err := loadVersion(versionSection, f.ByteOrder)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("load version: %w", err)
   122  	}
   123  
   124  	btfSpec, btfExtInfo, err := btf.LoadSpecAndExtInfosFromReader(rd)
   125  	if err != nil && !errors.Is(err, btf.ErrNotFound) {
   126  		return nil, fmt.Errorf("load BTF: %w", err)
   127  	}
   128  
   129  	ec := &elfCode{
   130  		SafeELFFile: f,
   131  		sections:    sections,
   132  		license:     license,
   133  		version:     version,
   134  		btf:         btfSpec,
   135  		extInfo:     btfExtInfo,
   136  		maps:        make(map[string]*MapSpec),
   137  		kfuncs:      make(map[string]*btf.Func),
   138  	}
   139  
   140  	symbols, err := f.Symbols()
   141  	if err != nil {
   142  		return nil, fmt.Errorf("load symbols: %v", err)
   143  	}
   144  
   145  	ec.assignSymbols(symbols)
   146  
   147  	if err := ec.loadRelocations(relSections, symbols); err != nil {
   148  		return nil, fmt.Errorf("load relocations: %w", err)
   149  	}
   150  
   151  	if err := ec.loadMaps(); err != nil {
   152  		return nil, fmt.Errorf("load maps: %w", err)
   153  	}
   154  
   155  	if err := ec.loadBTFMaps(); err != nil {
   156  		return nil, fmt.Errorf("load BTF maps: %w", err)
   157  	}
   158  
   159  	if err := ec.loadDataSections(); err != nil {
   160  		return nil, fmt.Errorf("load data sections: %w", err)
   161  	}
   162  
   163  	if err := ec.loadKconfigSection(); err != nil {
   164  		return nil, fmt.Errorf("load virtual .kconfig section: %w", err)
   165  	}
   166  
   167  	if err := ec.loadKsymsSection(); err != nil {
   168  		return nil, fmt.Errorf("load virtual .ksyms section: %w", err)
   169  	}
   170  
   171  	// Finally, collect programs and link them.
   172  	progs, err := ec.loadProgramSections()
   173  	if err != nil {
   174  		return nil, fmt.Errorf("load programs: %w", err)
   175  	}
   176  
   177  	return &CollectionSpec{ec.maps, progs, btfSpec, ec.ByteOrder}, nil
   178  }
   179  
   180  func loadLicense(sec *elf.Section) (string, error) {
   181  	if sec == nil {
   182  		return "", nil
   183  	}
   184  
   185  	data, err := sec.Data()
   186  	if err != nil {
   187  		return "", fmt.Errorf("section %s: %v", sec.Name, err)
   188  	}
   189  	return string(bytes.TrimRight(data, "\000")), nil
   190  }
   191  
   192  func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) {
   193  	if sec == nil {
   194  		return 0, nil
   195  	}
   196  
   197  	var version uint32
   198  	if err := binary.Read(sec.Open(), bo, &version); err != nil {
   199  		return 0, fmt.Errorf("section %s: %v", sec.Name, err)
   200  	}
   201  	return version, nil
   202  }
   203  
   204  type elfSectionKind int
   205  
   206  const (
   207  	undefSection elfSectionKind = iota
   208  	mapSection
   209  	btfMapSection
   210  	programSection
   211  	dataSection
   212  )
   213  
   214  type elfSection struct {
   215  	*elf.Section
   216  	kind elfSectionKind
   217  	// Offset from the start of the section to a symbol
   218  	symbols map[uint64]elf.Symbol
   219  	// Offset from the start of the section to a relocation, which points at
   220  	// a symbol in another section.
   221  	relocations map[uint64]elf.Symbol
   222  	// The number of relocations pointing at this section.
   223  	references int
   224  }
   225  
   226  func newElfSection(section *elf.Section, kind elfSectionKind) *elfSection {
   227  	return &elfSection{
   228  		section,
   229  		kind,
   230  		make(map[uint64]elf.Symbol),
   231  		make(map[uint64]elf.Symbol),
   232  		0,
   233  	}
   234  }
   235  
   236  // assignSymbols takes a list of symbols and assigns them to their
   237  // respective sections, indexed by name.
   238  func (ec *elfCode) assignSymbols(symbols []elf.Symbol) {
   239  	for _, symbol := range symbols {
   240  		symType := elf.ST_TYPE(symbol.Info)
   241  		symSection := ec.sections[symbol.Section]
   242  		if symSection == nil {
   243  			continue
   244  		}
   245  
   246  		// Anonymous symbols only occur in debug sections which we don't process
   247  		// relocations for. Anonymous symbols are not referenced from other sections.
   248  		if symbol.Name == "" {
   249  			continue
   250  		}
   251  
   252  		// Older versions of LLVM don't tag symbols correctly, so keep
   253  		// all NOTYPE ones.
   254  		switch symSection.kind {
   255  		case mapSection, btfMapSection, dataSection:
   256  			if symType != elf.STT_NOTYPE && symType != elf.STT_OBJECT {
   257  				continue
   258  			}
   259  		case programSection:
   260  			if symType != elf.STT_NOTYPE && symType != elf.STT_FUNC {
   261  				continue
   262  			}
   263  			// LLVM emits LBB_ (Local Basic Block) symbols that seem to be jump
   264  			// targets within sections, but BPF has no use for them.
   265  			if symType == elf.STT_NOTYPE && elf.ST_BIND(symbol.Info) == elf.STB_LOCAL &&
   266  				strings.HasPrefix(symbol.Name, "LBB") {
   267  				continue
   268  			}
   269  		// Only collect symbols that occur in program/maps/data sections.
   270  		default:
   271  			continue
   272  		}
   273  
   274  		symSection.symbols[symbol.Value] = symbol
   275  	}
   276  }
   277  
   278  // loadRelocations iterates .rel* sections and extracts relocation entries for
   279  // sections of interest. Makes sure relocations point at valid sections.
   280  func (ec *elfCode) loadRelocations(relSections map[elf.SectionIndex]*elf.Section, symbols []elf.Symbol) error {
   281  	for idx, relSection := range relSections {
   282  		section := ec.sections[idx]
   283  		if section == nil {
   284  			continue
   285  		}
   286  
   287  		rels, err := ec.loadSectionRelocations(relSection, symbols)
   288  		if err != nil {
   289  			return fmt.Errorf("relocation for section %q: %w", section.Name, err)
   290  		}
   291  
   292  		for _, rel := range rels {
   293  			target := ec.sections[rel.Section]
   294  			if target == nil {
   295  				return fmt.Errorf("section %q: reference to %q in section %s: %w", section.Name, rel.Name, rel.Section, ErrNotSupported)
   296  			}
   297  
   298  			target.references++
   299  		}
   300  
   301  		section.relocations = rels
   302  	}
   303  
   304  	return nil
   305  }
   306  
   307  // loadProgramSections iterates ec's sections and emits a ProgramSpec
   308  // for each function it finds.
   309  //
   310  // The resulting map is indexed by function name.
   311  func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {
   312  
   313  	progs := make(map[string]*ProgramSpec)
   314  
   315  	// Generate a ProgramSpec for each function found in each program section.
   316  	var export []string
   317  	for _, sec := range ec.sections {
   318  		if sec.kind != programSection {
   319  			continue
   320  		}
   321  
   322  		if len(sec.symbols) == 0 {
   323  			return nil, fmt.Errorf("section %v: missing symbols", sec.Name)
   324  		}
   325  
   326  		funcs, err := ec.loadFunctions(sec)
   327  		if err != nil {
   328  			return nil, fmt.Errorf("section %v: %w", sec.Name, err)
   329  		}
   330  
   331  		progType, attachType, progFlags, attachTo := getProgType(sec.Name)
   332  
   333  		for name, insns := range funcs {
   334  			spec := &ProgramSpec{
   335  				Name:          name,
   336  				Type:          progType,
   337  				Flags:         progFlags,
   338  				AttachType:    attachType,
   339  				AttachTo:      attachTo,
   340  				SectionName:   sec.Name,
   341  				License:       ec.license,
   342  				KernelVersion: ec.version,
   343  				Instructions:  insns,
   344  				ByteOrder:     ec.ByteOrder,
   345  			}
   346  
   347  			// Function names must be unique within a single ELF blob.
   348  			if progs[name] != nil {
   349  				return nil, fmt.Errorf("duplicate program name %s", name)
   350  			}
   351  			progs[name] = spec
   352  
   353  			if spec.SectionName != ".text" {
   354  				export = append(export, name)
   355  			}
   356  		}
   357  	}
   358  
   359  	flattenPrograms(progs, export)
   360  
   361  	// Hide programs (e.g. library functions) that were not explicitly emitted
   362  	// to an ELF section. These could be exposed in a separate CollectionSpec
   363  	// field later to allow them to be modified.
   364  	for n, p := range progs {
   365  		if p.SectionName == ".text" {
   366  			delete(progs, n)
   367  		}
   368  	}
   369  
   370  	return progs, nil
   371  }
   372  
   373  // loadFunctions extracts instruction streams from the given program section
   374  // starting at each symbol in the section. The section's symbols must already
   375  // be narrowed down to STT_NOTYPE (emitted by clang <8) or STT_FUNC.
   376  //
   377  // The resulting map is indexed by function name.
   378  func (ec *elfCode) loadFunctions(section *elfSection) (map[string]asm.Instructions, error) {
   379  	r := bufio.NewReader(section.Open())
   380  
   381  	// Decode the section's instruction stream.
   382  	insns := make(asm.Instructions, 0, section.Size/asm.InstructionSize)
   383  	if err := insns.Unmarshal(r, ec.ByteOrder); err != nil {
   384  		return nil, fmt.Errorf("decoding instructions for section %s: %w", section.Name, err)
   385  	}
   386  	if len(insns) == 0 {
   387  		return nil, fmt.Errorf("no instructions found in section %s", section.Name)
   388  	}
   389  
   390  	iter := insns.Iterate()
   391  	for iter.Next() {
   392  		ins := iter.Ins
   393  		offset := iter.Offset.Bytes()
   394  
   395  		// Tag Symbol Instructions.
   396  		if sym, ok := section.symbols[offset]; ok {
   397  			*ins = ins.WithSymbol(sym.Name)
   398  		}
   399  
   400  		// Apply any relocations for the current instruction.
   401  		// If no relocation is present, resolve any section-relative function calls.
   402  		if rel, ok := section.relocations[offset]; ok {
   403  			if err := ec.relocateInstruction(ins, rel); err != nil {
   404  				return nil, fmt.Errorf("offset %d: relocating instruction: %w", offset, err)
   405  			}
   406  		} else {
   407  			if err := referenceRelativeJump(ins, offset, section.symbols); err != nil {
   408  				return nil, fmt.Errorf("offset %d: resolving relative jump: %w", offset, err)
   409  			}
   410  		}
   411  	}
   412  
   413  	if ec.extInfo != nil {
   414  		ec.extInfo.Assign(insns, section.Name)
   415  	}
   416  
   417  	return splitSymbols(insns)
   418  }
   419  
   420  // referenceRelativeJump turns a relative jump to another bpf subprogram within
   421  // the same ELF section into a Reference Instruction.
   422  //
   423  // Up to LLVM 9, calls to subprograms within the same ELF section are sometimes
   424  // encoded using relative jumps instead of relocation entries. These jumps go
   425  // out of bounds of the current program, so their targets must be memoized
   426  // before the section's instruction stream is split.
   427  //
   428  // The relative jump Constant is blinded to -1 and the target Symbol is set as
   429  // the Instruction's Reference so it can be resolved by the linker.
   430  func referenceRelativeJump(ins *asm.Instruction, offset uint64, symbols map[uint64]elf.Symbol) error {
   431  	if !ins.IsFunctionReference() || ins.Constant == -1 {
   432  		return nil
   433  	}
   434  
   435  	tgt := jumpTarget(offset, *ins)
   436  	sym := symbols[tgt].Name
   437  	if sym == "" {
   438  		return fmt.Errorf("no jump target found at offset %d", tgt)
   439  	}
   440  
   441  	*ins = ins.WithReference(sym)
   442  	ins.Constant = -1
   443  
   444  	return nil
   445  }
   446  
   447  // jumpTarget takes ins' offset within an instruction stream (in bytes)
   448  // and returns its absolute jump destination (in bytes) within the
   449  // instruction stream.
   450  func jumpTarget(offset uint64, ins asm.Instruction) uint64 {
   451  	// A relative jump instruction describes the amount of raw BPF instructions
   452  	// to jump, convert the offset into bytes.
   453  	dest := ins.Constant * asm.InstructionSize
   454  
   455  	// The starting point of the jump is the end of the current instruction.
   456  	dest += int64(offset + asm.InstructionSize)
   457  
   458  	if dest < 0 {
   459  		return 0
   460  	}
   461  
   462  	return uint64(dest)
   463  }
   464  
   465  var errUnsupportedBinding = errors.New("unsupported binding")
   466  
   467  func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) error {
   468  	var (
   469  		typ  = elf.ST_TYPE(rel.Info)
   470  		bind = elf.ST_BIND(rel.Info)
   471  		name = rel.Name
   472  	)
   473  
   474  	target := ec.sections[rel.Section]
   475  
   476  	switch target.kind {
   477  	case mapSection, btfMapSection:
   478  		if bind == elf.STB_LOCAL {
   479  			return fmt.Errorf("possible erroneous static qualifier on map definition: found reference to %q", name)
   480  		}
   481  
   482  		if bind != elf.STB_GLOBAL {
   483  			return fmt.Errorf("map %q: %w: %s", name, errUnsupportedBinding, bind)
   484  		}
   485  
   486  		if typ != elf.STT_OBJECT && typ != elf.STT_NOTYPE {
   487  			// STT_NOTYPE is generated on clang < 8 which doesn't tag
   488  			// relocations appropriately.
   489  			return fmt.Errorf("map load: incorrect relocation type %v", typ)
   490  		}
   491  
   492  		ins.Src = asm.PseudoMapFD
   493  
   494  	case dataSection:
   495  		var offset uint32
   496  		switch typ {
   497  		case elf.STT_SECTION:
   498  			if bind != elf.STB_LOCAL {
   499  				return fmt.Errorf("direct load: %s: %w: %s", name, errUnsupportedBinding, bind)
   500  			}
   501  
   502  			// This is really a reference to a static symbol, which clang doesn't
   503  			// emit a symbol table entry for. Instead it encodes the offset in
   504  			// the instruction itself.
   505  			offset = uint32(uint64(ins.Constant))
   506  
   507  		case elf.STT_OBJECT:
   508  			// LLVM 9 emits OBJECT-LOCAL symbols for anonymous constants.
   509  			if bind != elf.STB_GLOBAL && bind != elf.STB_LOCAL {
   510  				return fmt.Errorf("direct load: %s: %w: %s", name, errUnsupportedBinding, bind)
   511  			}
   512  
   513  			offset = uint32(rel.Value)
   514  
   515  		case elf.STT_NOTYPE:
   516  			// LLVM 7 emits NOTYPE-LOCAL symbols for anonymous constants.
   517  			if bind != elf.STB_LOCAL {
   518  				return fmt.Errorf("direct load: %s: %w: %s", name, errUnsupportedBinding, bind)
   519  			}
   520  
   521  			offset = uint32(rel.Value)
   522  
   523  		default:
   524  			return fmt.Errorf("incorrect relocation type %v for direct map load", typ)
   525  		}
   526  
   527  		// We rely on using the name of the data section as the reference. It
   528  		// would be nicer to keep the real name in case of an STT_OBJECT, but
   529  		// it's not clear how to encode that into Instruction.
   530  		name = target.Name
   531  
   532  		// The kernel expects the offset in the second basic BPF instruction.
   533  		ins.Constant = int64(uint64(offset) << 32)
   534  		ins.Src = asm.PseudoMapValue
   535  
   536  	case programSection:
   537  		switch opCode := ins.OpCode; {
   538  		case opCode.JumpOp() == asm.Call:
   539  			if ins.Src != asm.PseudoCall {
   540  				return fmt.Errorf("call: %s: incorrect source register", name)
   541  			}
   542  
   543  			switch typ {
   544  			case elf.STT_NOTYPE, elf.STT_FUNC:
   545  				if bind != elf.STB_GLOBAL {
   546  					return fmt.Errorf("call: %s: %w: %s", name, errUnsupportedBinding, bind)
   547  				}
   548  
   549  			case elf.STT_SECTION:
   550  				if bind != elf.STB_LOCAL {
   551  					return fmt.Errorf("call: %s: %w: %s", name, errUnsupportedBinding, bind)
   552  				}
   553  
   554  				// The function we want to call is in the indicated section,
   555  				// at the offset encoded in the instruction itself. Reverse
   556  				// the calculation to find the real function we're looking for.
   557  				// A value of -1 references the first instruction in the section.
   558  				offset := int64(int32(ins.Constant)+1) * asm.InstructionSize
   559  				sym, ok := target.symbols[uint64(offset)]
   560  				if !ok {
   561  					return fmt.Errorf("call: no symbol at offset %d", offset)
   562  				}
   563  
   564  				name = sym.Name
   565  				ins.Constant = -1
   566  
   567  			default:
   568  				return fmt.Errorf("call: %s: invalid symbol type %s", name, typ)
   569  			}
   570  		case opCode.IsDWordLoad():
   571  			switch typ {
   572  			case elf.STT_FUNC:
   573  				if bind != elf.STB_GLOBAL {
   574  					return fmt.Errorf("load: %s: %w: %s", name, errUnsupportedBinding, bind)
   575  				}
   576  
   577  			case elf.STT_SECTION:
   578  				if bind != elf.STB_LOCAL {
   579  					return fmt.Errorf("load: %s: %w: %s", name, errUnsupportedBinding, bind)
   580  				}
   581  
   582  				// ins.Constant already contains the offset in bytes from the
   583  				// start of the section. This is different than a call to a
   584  				// static function.
   585  
   586  			default:
   587  				return fmt.Errorf("load: %s: invalid symbol type %s", name, typ)
   588  			}
   589  
   590  			sym, ok := target.symbols[uint64(ins.Constant)]
   591  			if !ok {
   592  				return fmt.Errorf("load: no symbol at offset %d", ins.Constant)
   593  			}
   594  
   595  			name = sym.Name
   596  			ins.Constant = -1
   597  			ins.Src = asm.PseudoFunc
   598  
   599  		default:
   600  			return fmt.Errorf("neither a call nor a load instruction: %v", ins)
   601  		}
   602  
   603  	// The Undefined section is used for 'virtual' symbols that aren't backed by
   604  	// an ELF section. This includes symbol references from inline asm, forward
   605  	// function declarations, as well as extern kfunc declarations using __ksym
   606  	// and extern kconfig variables declared using __kconfig.
   607  	case undefSection:
   608  		if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK {
   609  			return fmt.Errorf("asm relocation: %s: %w: %s", name, errUnsupportedBinding, bind)
   610  		}
   611  
   612  		if typ != elf.STT_NOTYPE {
   613  			return fmt.Errorf("asm relocation: %s: unsupported type %s", name, typ)
   614  		}
   615  
   616  		kf := ec.kfuncs[name]
   617  		switch {
   618  		// If a Call / DWordLoad instruction is found and the datasec has a btf.Func with a Name
   619  		// that matches the symbol name we mark the instruction as a referencing a kfunc.
   620  		case kf != nil && ins.OpCode.JumpOp() == asm.Call:
   621  			ins.Metadata.Set(kfuncMetaKey{}, &kfuncMeta{
   622  				Func:    kf,
   623  				Binding: bind,
   624  			})
   625  
   626  			ins.Src = asm.PseudoKfuncCall
   627  			ins.Constant = -1
   628  
   629  		case kf != nil && ins.OpCode.IsDWordLoad():
   630  			ins.Metadata.Set(kfuncMetaKey{}, &kfuncMeta{
   631  				Func:    kf,
   632  				Binding: bind,
   633  			})
   634  
   635  			ins.Constant = 0
   636  
   637  		// If no kconfig map is found, this must be a symbol reference from inline
   638  		// asm (see testdata/loader.c:asm_relocation()) or a call to a forward
   639  		// function declaration (see testdata/fwd_decl.c). Don't interfere, These
   640  		// remain standard symbol references.
   641  		// extern __kconfig reads are represented as dword loads that need to be
   642  		// rewritten to pseudo map loads from .kconfig. If the map is present,
   643  		// require it to contain the symbol to disambiguate between inline asm
   644  		// relos and kconfigs.
   645  		case ec.kconfig != nil && ins.OpCode.IsDWordLoad():
   646  			if bind != elf.STB_GLOBAL {
   647  				return fmt.Errorf("asm relocation: %s: %w: %s", name, errUnsupportedBinding, bind)
   648  			}
   649  
   650  			for _, vsi := range ec.kconfig.Value.(*btf.Datasec).Vars {
   651  				if vsi.Type.(*btf.Var).Name != rel.Name {
   652  					continue
   653  				}
   654  
   655  				ins.Src = asm.PseudoMapValue
   656  				ins.Metadata.Set(kconfigMetaKey{}, &kconfigMeta{ec.kconfig, vsi.Offset})
   657  				return nil
   658  			}
   659  
   660  			return fmt.Errorf("kconfig %s not found in .kconfig", rel.Name)
   661  		}
   662  
   663  	default:
   664  		return fmt.Errorf("relocation to %q: %w", target.Name, ErrNotSupported)
   665  	}
   666  
   667  	*ins = ins.WithReference(name)
   668  	return nil
   669  }
   670  
   671  func (ec *elfCode) loadMaps() error {
   672  	for _, sec := range ec.sections {
   673  		if sec.kind != mapSection {
   674  			continue
   675  		}
   676  
   677  		nSym := len(sec.symbols)
   678  		if nSym == 0 {
   679  			return fmt.Errorf("section %v: no symbols", sec.Name)
   680  		}
   681  
   682  		if sec.Size%uint64(nSym) != 0 {
   683  			return fmt.Errorf("section %v: map descriptors are not of equal size", sec.Name)
   684  		}
   685  
   686  		var (
   687  			r    = bufio.NewReader(sec.Open())
   688  			size = sec.Size / uint64(nSym)
   689  		)
   690  		for i, offset := 0, uint64(0); i < nSym; i, offset = i+1, offset+size {
   691  			mapSym, ok := sec.symbols[offset]
   692  			if !ok {
   693  				return fmt.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
   694  			}
   695  
   696  			mapName := mapSym.Name
   697  			if ec.maps[mapName] != nil {
   698  				return fmt.Errorf("section %v: map %v already exists", sec.Name, mapSym)
   699  			}
   700  
   701  			lr := io.LimitReader(r, int64(size))
   702  
   703  			spec := MapSpec{
   704  				Name: SanitizeName(mapName, -1),
   705  			}
   706  			switch {
   707  			case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
   708  				return fmt.Errorf("map %s: missing type", mapName)
   709  			case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil:
   710  				return fmt.Errorf("map %s: missing key size", mapName)
   711  			case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil:
   712  				return fmt.Errorf("map %s: missing value size", mapName)
   713  			case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil:
   714  				return fmt.Errorf("map %s: missing max entries", mapName)
   715  			case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil:
   716  				return fmt.Errorf("map %s: missing flags", mapName)
   717  			}
   718  
   719  			extra, err := io.ReadAll(lr)
   720  			if err != nil {
   721  				return fmt.Errorf("map %s: reading map tail: %w", mapName, err)
   722  			}
   723  			if len(extra) > 0 {
   724  				spec.Extra = bytes.NewReader(extra)
   725  			}
   726  
   727  			ec.maps[mapName] = &spec
   728  		}
   729  	}
   730  
   731  	return nil
   732  }
   733  
   734  // loadBTFMaps iterates over all ELF sections marked as BTF map sections
   735  // (like .maps) and parses them into MapSpecs. Dump the .maps section and
   736  // any relocations with `readelf -x .maps -r <elf_file>`.
   737  func (ec *elfCode) loadBTFMaps() error {
   738  	for _, sec := range ec.sections {
   739  		if sec.kind != btfMapSection {
   740  			continue
   741  		}
   742  
   743  		if ec.btf == nil {
   744  			return fmt.Errorf("missing BTF")
   745  		}
   746  
   747  		// Each section must appear as a DataSec in the ELF's BTF blob.
   748  		var ds *btf.Datasec
   749  		if err := ec.btf.TypeByName(sec.Name, &ds); err != nil {
   750  			return fmt.Errorf("cannot find section '%s' in BTF: %w", sec.Name, err)
   751  		}
   752  
   753  		// Open a Reader to the ELF's raw section bytes so we can assert that all
   754  		// of them are zero on a per-map (per-Var) basis. For now, the section's
   755  		// sole purpose is to receive relocations, so all must be zero.
   756  		rs := sec.Open()
   757  
   758  		for _, vs := range ds.Vars {
   759  			// BPF maps are declared as and assigned to global variables,
   760  			// so iterate over each Var in the DataSec and validate their types.
   761  			v, ok := vs.Type.(*btf.Var)
   762  			if !ok {
   763  				return fmt.Errorf("section %v: unexpected type %s", sec.Name, vs.Type)
   764  			}
   765  			name := string(v.Name)
   766  
   767  			// The BTF metadata for each Var contains the full length of the map
   768  			// declaration, so read the corresponding amount of bytes from the ELF.
   769  			// This way, we can pinpoint which map declaration contains unexpected
   770  			// (and therefore unsupported) data.
   771  			_, err := io.Copy(internal.DiscardZeroes{}, io.LimitReader(rs, int64(vs.Size)))
   772  			if err != nil {
   773  				return fmt.Errorf("section %v: map %s: initializing BTF map definitions: %w", sec.Name, name, internal.ErrNotSupported)
   774  			}
   775  
   776  			if ec.maps[name] != nil {
   777  				return fmt.Errorf("section %v: map %s already exists", sec.Name, name)
   778  			}
   779  
   780  			// Each Var representing a BTF map definition contains a Struct.
   781  			mapStruct, ok := btf.UnderlyingType(v.Type).(*btf.Struct)
   782  			if !ok {
   783  				return fmt.Errorf("expected struct, got %s", v.Type)
   784  			}
   785  
   786  			mapSpec, err := mapSpecFromBTF(sec, &vs, mapStruct, ec.btf, name, false)
   787  			if err != nil {
   788  				return fmt.Errorf("map %v: %w", name, err)
   789  			}
   790  
   791  			ec.maps[name] = mapSpec
   792  		}
   793  
   794  		// Drain the ELF section reader to make sure all bytes are accounted for
   795  		// with BTF metadata.
   796  		i, err := io.Copy(io.Discard, rs)
   797  		if err != nil {
   798  			return fmt.Errorf("section %v: unexpected error reading remainder of ELF section: %w", sec.Name, err)
   799  		}
   800  		if i > 0 {
   801  			return fmt.Errorf("section %v: %d unexpected remaining bytes in ELF section, invalid BTF?", sec.Name, i)
   802  		}
   803  	}
   804  
   805  	return nil
   806  }
   807  
   808  // mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing
   809  // a BTF map definition. The name and spec arguments will be copied to the
   810  // resulting MapSpec, and inner must be true on any recursive invocations.
   811  func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) {
   812  	var (
   813  		key, value         btf.Type
   814  		keySize, valueSize uint32
   815  		mapType            MapType
   816  		flags, maxEntries  uint32
   817  		pinType            PinType
   818  		innerMapSpec       *MapSpec
   819  		contents           []MapKV
   820  		err                error
   821  	)
   822  
   823  	for i, member := range def.Members {
   824  		switch member.Name {
   825  		case "type":
   826  			mt, err := uintFromBTF(member.Type)
   827  			if err != nil {
   828  				return nil, fmt.Errorf("can't get type: %w", err)
   829  			}
   830  			mapType = MapType(mt)
   831  
   832  		case "map_flags":
   833  			flags, err = uintFromBTF(member.Type)
   834  			if err != nil {
   835  				return nil, fmt.Errorf("can't get BTF map flags: %w", err)
   836  			}
   837  
   838  		case "max_entries":
   839  			maxEntries, err = uintFromBTF(member.Type)
   840  			if err != nil {
   841  				return nil, fmt.Errorf("can't get BTF map max entries: %w", err)
   842  			}
   843  
   844  		case "key":
   845  			if keySize != 0 {
   846  				return nil, errors.New("both key and key_size given")
   847  			}
   848  
   849  			pk, ok := member.Type.(*btf.Pointer)
   850  			if !ok {
   851  				return nil, fmt.Errorf("key type is not a pointer: %T", member.Type)
   852  			}
   853  
   854  			key = pk.Target
   855  
   856  			size, err := btf.Sizeof(pk.Target)
   857  			if err != nil {
   858  				return nil, fmt.Errorf("can't get size of BTF key: %w", err)
   859  			}
   860  
   861  			keySize = uint32(size)
   862  
   863  		case "value":
   864  			if valueSize != 0 {
   865  				return nil, errors.New("both value and value_size given")
   866  			}
   867  
   868  			vk, ok := member.Type.(*btf.Pointer)
   869  			if !ok {
   870  				return nil, fmt.Errorf("value type is not a pointer: %T", member.Type)
   871  			}
   872  
   873  			value = vk.Target
   874  
   875  			size, err := btf.Sizeof(vk.Target)
   876  			if err != nil {
   877  				return nil, fmt.Errorf("can't get size of BTF value: %w", err)
   878  			}
   879  
   880  			valueSize = uint32(size)
   881  
   882  		case "key_size":
   883  			// Key needs to be nil and keySize needs to be 0 for key_size to be
   884  			// considered a valid member.
   885  			if key != nil || keySize != 0 {
   886  				return nil, errors.New("both key and key_size given")
   887  			}
   888  
   889  			keySize, err = uintFromBTF(member.Type)
   890  			if err != nil {
   891  				return nil, fmt.Errorf("can't get BTF key size: %w", err)
   892  			}
   893  
   894  		case "value_size":
   895  			// Value needs to be nil and valueSize needs to be 0 for value_size to be
   896  			// considered a valid member.
   897  			if value != nil || valueSize != 0 {
   898  				return nil, errors.New("both value and value_size given")
   899  			}
   900  
   901  			valueSize, err = uintFromBTF(member.Type)
   902  			if err != nil {
   903  				return nil, fmt.Errorf("can't get BTF value size: %w", err)
   904  			}
   905  
   906  		case "pinning":
   907  			if inner {
   908  				return nil, errors.New("inner maps can't be pinned")
   909  			}
   910  
   911  			pinning, err := uintFromBTF(member.Type)
   912  			if err != nil {
   913  				return nil, fmt.Errorf("can't get pinning: %w", err)
   914  			}
   915  
   916  			pinType = PinType(pinning)
   917  
   918  		case "values":
   919  			// The 'values' field in BTF map definitions is used for declaring map
   920  			// value types that are references to other BPF objects, like other maps
   921  			// or programs. It is always expected to be an array of pointers.
   922  			if i != len(def.Members)-1 {
   923  				return nil, errors.New("'values' must be the last member in a BTF map definition")
   924  			}
   925  
   926  			if valueSize != 0 && valueSize != 4 {
   927  				return nil, errors.New("value_size must be 0 or 4")
   928  			}
   929  			valueSize = 4
   930  
   931  			valueType, err := resolveBTFArrayMacro(member.Type)
   932  			if err != nil {
   933  				return nil, fmt.Errorf("can't resolve type of member 'values': %w", err)
   934  			}
   935  
   936  			switch t := valueType.(type) {
   937  			case *btf.Struct:
   938  				// The values member pointing to an array of structs means we're expecting
   939  				// a map-in-map declaration.
   940  				if mapType != ArrayOfMaps && mapType != HashOfMaps {
   941  					return nil, errors.New("outer map needs to be an array or a hash of maps")
   942  				}
   943  				if inner {
   944  					return nil, fmt.Errorf("nested inner maps are not supported")
   945  				}
   946  
   947  				// This inner map spec is used as a map template, but it needs to be
   948  				// created as a traditional map before it can be used to do so.
   949  				// libbpf names the inner map template '<outer_name>.inner', but we
   950  				// opted for _inner to simplify validation logic. (dots only supported
   951  				// on kernels 5.2 and up)
   952  				// Pass the BTF spec from the parent object, since both parent and
   953  				// child must be created from the same BTF blob (on kernels that support BTF).
   954  				innerMapSpec, err = mapSpecFromBTF(es, vs, t, spec, name+"_inner", true)
   955  				if err != nil {
   956  					return nil, fmt.Errorf("can't parse BTF map definition of inner map: %w", err)
   957  				}
   958  
   959  			case *btf.FuncProto:
   960  				// The values member contains an array of function pointers, meaning an
   961  				// autopopulated PROG_ARRAY.
   962  				if mapType != ProgramArray {
   963  					return nil, errors.New("map needs to be a program array")
   964  				}
   965  
   966  			default:
   967  				return nil, fmt.Errorf("unsupported value type %q in 'values' field", t)
   968  			}
   969  
   970  			contents, err = resolveBTFValuesContents(es, vs, member)
   971  			if err != nil {
   972  				return nil, fmt.Errorf("resolving values contents: %w", err)
   973  			}
   974  
   975  		default:
   976  			return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name)
   977  		}
   978  	}
   979  
   980  	return &MapSpec{
   981  		Name:       SanitizeName(name, -1),
   982  		Type:       MapType(mapType),
   983  		KeySize:    keySize,
   984  		ValueSize:  valueSize,
   985  		MaxEntries: maxEntries,
   986  		Flags:      flags,
   987  		Key:        key,
   988  		Value:      value,
   989  		Pinning:    pinType,
   990  		InnerMap:   innerMapSpec,
   991  		Contents:   contents,
   992  	}, nil
   993  }
   994  
   995  // uintFromBTF resolves the __uint macro, which is a pointer to a sized
   996  // array, e.g. for int (*foo)[10], this function will return 10.
   997  func uintFromBTF(typ btf.Type) (uint32, error) {
   998  	ptr, ok := typ.(*btf.Pointer)
   999  	if !ok {
  1000  		return 0, fmt.Errorf("not a pointer: %v", typ)
  1001  	}
  1002  
  1003  	arr, ok := ptr.Target.(*btf.Array)
  1004  	if !ok {
  1005  		return 0, fmt.Errorf("not a pointer to array: %v", typ)
  1006  	}
  1007  
  1008  	return arr.Nelems, nil
  1009  }
  1010  
  1011  // resolveBTFArrayMacro resolves the __array macro, which declares an array
  1012  // of pointers to a given type. This function returns the target Type of
  1013  // the pointers in the array.
  1014  func resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) {
  1015  	arr, ok := typ.(*btf.Array)
  1016  	if !ok {
  1017  		return nil, fmt.Errorf("not an array: %v", typ)
  1018  	}
  1019  
  1020  	ptr, ok := arr.Type.(*btf.Pointer)
  1021  	if !ok {
  1022  		return nil, fmt.Errorf("not an array of pointers: %v", typ)
  1023  	}
  1024  
  1025  	return ptr.Target, nil
  1026  }
  1027  
  1028  // resolveBTFValuesContents resolves relocations into ELF sections belonging
  1029  // to btf.VarSecinfo's. This can be used on the 'values' member in BTF map
  1030  // definitions to extract static declarations of map contents.
  1031  func resolveBTFValuesContents(es *elfSection, vs *btf.VarSecinfo, member btf.Member) ([]MapKV, error) {
  1032  	// The elements of a .values pointer array are not encoded in BTF.
  1033  	// Instead, relocations are generated into each array index.
  1034  	// However, it's possible to leave certain array indices empty, so all
  1035  	// indices' offsets need to be checked for emitted relocations.
  1036  
  1037  	// The offset of the 'values' member within the _struct_ (in bits)
  1038  	// is the starting point of the array. Convert to bytes. Add VarSecinfo
  1039  	// offset to get the absolute position in the ELF blob.
  1040  	start := member.Offset.Bytes() + vs.Offset
  1041  	// 'values' is encoded in BTF as a zero (variable) length struct
  1042  	// member, and its contents run until the end of the VarSecinfo.
  1043  	// Add VarSecinfo offset to get the absolute position in the ELF blob.
  1044  	end := vs.Size + vs.Offset
  1045  	// The size of an address in this section. This determines the width of
  1046  	// an index in the array.
  1047  	align := uint32(es.SectionHeader.Addralign)
  1048  
  1049  	// Check if variable-length section is aligned.
  1050  	if (end-start)%align != 0 {
  1051  		return nil, errors.New("unaligned static values section")
  1052  	}
  1053  	elems := (end - start) / align
  1054  
  1055  	if elems == 0 {
  1056  		return nil, nil
  1057  	}
  1058  
  1059  	contents := make([]MapKV, 0, elems)
  1060  
  1061  	// k is the array index, off is its corresponding ELF section offset.
  1062  	for k, off := uint32(0), start; k < elems; k, off = k+1, off+align {
  1063  		r, ok := es.relocations[uint64(off)]
  1064  		if !ok {
  1065  			continue
  1066  		}
  1067  
  1068  		// Relocation exists for the current offset in the ELF section.
  1069  		// Emit a value stub based on the type of relocation to be replaced by
  1070  		// a real fd later in the pipeline before populating the map.
  1071  		// Map keys are encoded in MapKV entries, so empty array indices are
  1072  		// skipped here.
  1073  		switch t := elf.ST_TYPE(r.Info); t {
  1074  		case elf.STT_FUNC:
  1075  			contents = append(contents, MapKV{uint32(k), r.Name})
  1076  		case elf.STT_OBJECT:
  1077  			contents = append(contents, MapKV{uint32(k), r.Name})
  1078  		default:
  1079  			return nil, fmt.Errorf("unknown relocation type %v for symbol %s", t, r.Name)
  1080  		}
  1081  	}
  1082  
  1083  	return contents, nil
  1084  }
  1085  
  1086  func (ec *elfCode) loadDataSections() error {
  1087  	for _, sec := range ec.sections {
  1088  		if sec.kind != dataSection {
  1089  			continue
  1090  		}
  1091  
  1092  		if sec.references == 0 {
  1093  			// Prune data sections which are not referenced by any
  1094  			// instructions.
  1095  			continue
  1096  		}
  1097  
  1098  		mapSpec := &MapSpec{
  1099  			Name:       SanitizeName(sec.Name, -1),
  1100  			Type:       Array,
  1101  			KeySize:    4,
  1102  			ValueSize:  uint32(sec.Size),
  1103  			MaxEntries: 1,
  1104  		}
  1105  
  1106  		switch sec.Type {
  1107  		// Only open the section if we know there's actual data to be read.
  1108  		case elf.SHT_PROGBITS:
  1109  			data, err := sec.Data()
  1110  			if err != nil {
  1111  				return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err)
  1112  			}
  1113  
  1114  			if uint64(len(data)) > math.MaxUint32 {
  1115  				return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name)
  1116  			}
  1117  			mapSpec.Contents = []MapKV{{uint32(0), data}}
  1118  
  1119  		case elf.SHT_NOBITS:
  1120  			// NOBITS sections like .bss contain only zeroes, and since data sections
  1121  			// are Arrays, the kernel already preallocates them. Skip reading zeroes
  1122  			// from the ELF.
  1123  		default:
  1124  			return fmt.Errorf("data section %s: unknown section type %s", sec.Name, sec.Type)
  1125  		}
  1126  
  1127  		// It is possible for a data section to exist without a corresponding BTF Datasec
  1128  		// if it only contains anonymous values like macro-defined arrays.
  1129  		if ec.btf != nil {
  1130  			var ds *btf.Datasec
  1131  			if ec.btf.TypeByName(sec.Name, &ds) == nil {
  1132  				// Assign the spec's key and BTF only if the Datasec lookup was successful.
  1133  				mapSpec.Key = &btf.Void{}
  1134  				mapSpec.Value = ds
  1135  			}
  1136  		}
  1137  
  1138  		if strings.HasPrefix(sec.Name, ".rodata") {
  1139  			mapSpec.Flags = unix.BPF_F_RDONLY_PROG
  1140  			mapSpec.Freeze = true
  1141  		}
  1142  
  1143  		ec.maps[sec.Name] = mapSpec
  1144  	}
  1145  
  1146  	return nil
  1147  }
  1148  
  1149  // loadKconfigSection handles the 'virtual' Datasec .kconfig that doesn't
  1150  // have a corresponding ELF section and exist purely in BTF.
  1151  func (ec *elfCode) loadKconfigSection() error {
  1152  	if ec.btf == nil {
  1153  		return nil
  1154  	}
  1155  
  1156  	var ds *btf.Datasec
  1157  	err := ec.btf.TypeByName(".kconfig", &ds)
  1158  	if errors.Is(err, btf.ErrNotFound) {
  1159  		return nil
  1160  	}
  1161  	if err != nil {
  1162  		return err
  1163  	}
  1164  
  1165  	if ds.Size == 0 {
  1166  		return errors.New("zero-length .kconfig")
  1167  	}
  1168  
  1169  	ec.kconfig = &MapSpec{
  1170  		Name:       ".kconfig",
  1171  		Type:       Array,
  1172  		KeySize:    uint32(4),
  1173  		ValueSize:  ds.Size,
  1174  		MaxEntries: 1,
  1175  		Flags:      unix.BPF_F_RDONLY_PROG,
  1176  		Freeze:     true,
  1177  		Key:        &btf.Int{Size: 4},
  1178  		Value:      ds,
  1179  	}
  1180  
  1181  	return nil
  1182  }
  1183  
  1184  // loadKsymsSection handles the 'virtual' Datasec .ksyms that doesn't
  1185  // have a corresponding ELF section and exist purely in BTF.
  1186  func (ec *elfCode) loadKsymsSection() error {
  1187  	if ec.btf == nil {
  1188  		return nil
  1189  	}
  1190  
  1191  	var ds *btf.Datasec
  1192  	err := ec.btf.TypeByName(".ksyms", &ds)
  1193  	if errors.Is(err, btf.ErrNotFound) {
  1194  		return nil
  1195  	}
  1196  	if err != nil {
  1197  		return err
  1198  	}
  1199  
  1200  	for _, v := range ds.Vars {
  1201  		// we have already checked the .ksyms Datasec to only contain Func Vars.
  1202  		ec.kfuncs[v.Type.TypeName()] = v.Type.(*btf.Func)
  1203  	}
  1204  
  1205  	return nil
  1206  }
  1207  
  1208  type libbpfElfSectionDef struct {
  1209  	pattern     string
  1210  	programType sys.ProgType
  1211  	attachType  sys.AttachType
  1212  	flags       libbpfElfSectionFlag
  1213  }
  1214  
  1215  type libbpfElfSectionFlag uint32
  1216  
  1217  // The values correspond to enum sec_def_flags in libbpf.
  1218  const (
  1219  	_SEC_NONE libbpfElfSectionFlag = 0
  1220  
  1221  	_SEC_EXP_ATTACH_OPT libbpfElfSectionFlag = 1 << (iota - 1)
  1222  	_SEC_ATTACHABLE
  1223  	_SEC_ATTACH_BTF
  1224  	_SEC_SLEEPABLE
  1225  	_SEC_XDP_FRAGS
  1226  	_SEC_USDT
  1227  
  1228  	// Ignore any present extra in order to preserve backwards compatibility
  1229  	// with earlier versions of the library.
  1230  	ignoreExtra
  1231  
  1232  	_SEC_ATTACHABLE_OPT = _SEC_ATTACHABLE | _SEC_EXP_ATTACH_OPT
  1233  )
  1234  
  1235  func init() {
  1236  	// Compatibility with older versions of the library.
  1237  	// We prepend libbpf definitions since they contain a prefix match
  1238  	// for "xdp".
  1239  	elfSectionDefs = append([]libbpfElfSectionDef{
  1240  		{"xdp.frags/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP, _SEC_XDP_FRAGS | ignoreExtra},
  1241  		{"xdp.frags_devmap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, _SEC_XDP_FRAGS},
  1242  		{"xdp_devmap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, 0},
  1243  		{"xdp.frags_cpumap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, _SEC_XDP_FRAGS},
  1244  		{"xdp_cpumap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, 0},
  1245  		// This has been in the library since the beginning of time. Not sure
  1246  		// where it came from.
  1247  		{"seccomp", sys.BPF_PROG_TYPE_SOCKET_FILTER, 0, _SEC_NONE},
  1248  	}, elfSectionDefs...)
  1249  }
  1250  
  1251  func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
  1252  	// Skip optional program marking for now.
  1253  	sectionName = strings.TrimPrefix(sectionName, "?")
  1254  
  1255  	for _, t := range elfSectionDefs {
  1256  		extra, ok := matchSectionName(sectionName, t.pattern)
  1257  		if !ok {
  1258  			continue
  1259  		}
  1260  
  1261  		programType := ProgramType(t.programType)
  1262  		attachType := AttachType(t.attachType)
  1263  
  1264  		var flags uint32
  1265  		if t.flags&_SEC_SLEEPABLE > 0 {
  1266  			flags |= unix.BPF_F_SLEEPABLE
  1267  		}
  1268  		if t.flags&_SEC_XDP_FRAGS > 0 {
  1269  			flags |= unix.BPF_F_XDP_HAS_FRAGS
  1270  		}
  1271  		if t.flags&_SEC_EXP_ATTACH_OPT > 0 {
  1272  			if programType == XDP {
  1273  				// The library doesn't yet have code to fallback to not specifying
  1274  				// attach type. Only do this for XDP since we've enforced correct
  1275  				// attach type for all other program types.
  1276  				attachType = AttachNone
  1277  			}
  1278  		}
  1279  		if t.flags&ignoreExtra > 0 {
  1280  			extra = ""
  1281  		}
  1282  
  1283  		return programType, attachType, flags, extra
  1284  	}
  1285  
  1286  	return UnspecifiedProgram, AttachNone, 0, ""
  1287  }
  1288  
  1289  // matchSectionName checks a section name against a pattern.
  1290  //
  1291  // It's behaviour mirrors that of libbpf's sec_def_matches.
  1292  func matchSectionName(sectionName, pattern string) (extra string, found bool) {
  1293  	have, extra, found := strings.Cut(sectionName, "/")
  1294  	want := strings.TrimRight(pattern, "+/")
  1295  
  1296  	if strings.HasSuffix(pattern, "/") {
  1297  		// Section name must have a slash and extra may be empty.
  1298  		return extra, have == want && found
  1299  	} else if strings.HasSuffix(pattern, "+") {
  1300  		// Section name may have a slash and extra may be empty.
  1301  		return extra, have == want
  1302  	}
  1303  
  1304  	// Section name must have a prefix. extra is ignored.
  1305  	return "", strings.HasPrefix(sectionName, pattern)
  1306  }
  1307  
  1308  func (ec *elfCode) loadSectionRelocations(sec *elf.Section, symbols []elf.Symbol) (map[uint64]elf.Symbol, error) {
  1309  	rels := make(map[uint64]elf.Symbol)
  1310  
  1311  	if sec.Entsize < 16 {
  1312  		return nil, fmt.Errorf("section %s: relocations are less than 16 bytes", sec.Name)
  1313  	}
  1314  
  1315  	r := bufio.NewReader(sec.Open())
  1316  	for off := uint64(0); off < sec.Size; off += sec.Entsize {
  1317  		ent := io.LimitReader(r, int64(sec.Entsize))
  1318  
  1319  		var rel elf.Rel64
  1320  		if binary.Read(ent, ec.ByteOrder, &rel) != nil {
  1321  			return nil, fmt.Errorf("can't parse relocation at offset %v", off)
  1322  		}
  1323  
  1324  		symNo := int(elf.R_SYM64(rel.Info) - 1)
  1325  		if symNo >= len(symbols) {
  1326  			return nil, fmt.Errorf("offset %d: symbol %d doesn't exist", off, symNo)
  1327  		}
  1328  
  1329  		symbol := symbols[symNo]
  1330  		rels[rel.Off] = symbol
  1331  	}
  1332  
  1333  	return rels, nil
  1334  }