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