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