gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

elf_reader.go (36585B)


      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 }