...

Source file src/github.com/cilium/ebpf/elf_reader.go

Documentation: github.com/cilium/ebpf

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

View as plain text