github.com/cilium/ebpf@v0.16.0/elf_reader.go (about) 1 package ebpf 2 3 import ( 4 "bufio" 5 "bytes" 6 "debug/elf" 7 "encoding/binary" 8 "errors" 9 "fmt" 10 "io" 11 "math" 12 "os" 13 "strings" 14 15 "github.com/cilium/ebpf/asm" 16 "github.com/cilium/ebpf/btf" 17 "github.com/cilium/ebpf/internal" 18 "github.com/cilium/ebpf/internal/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 case "map_extra": 976 return nil, fmt.Errorf("BTF map definition: field %s: %w", member.Name, ErrNotSupported) 977 978 default: 979 return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name) 980 } 981 } 982 983 return &MapSpec{ 984 Name: SanitizeName(name, -1), 985 Type: MapType(mapType), 986 KeySize: keySize, 987 ValueSize: valueSize, 988 MaxEntries: maxEntries, 989 Flags: flags, 990 Key: key, 991 Value: value, 992 Pinning: pinType, 993 InnerMap: innerMapSpec, 994 Contents: contents, 995 }, nil 996 } 997 998 // uintFromBTF resolves the __uint macro, which is a pointer to a sized 999 // array, e.g. for int (*foo)[10], this function will return 10. 1000 func uintFromBTF(typ btf.Type) (uint32, error) { 1001 ptr, ok := typ.(*btf.Pointer) 1002 if !ok { 1003 return 0, fmt.Errorf("not a pointer: %v", typ) 1004 } 1005 1006 arr, ok := ptr.Target.(*btf.Array) 1007 if !ok { 1008 return 0, fmt.Errorf("not a pointer to array: %v", typ) 1009 } 1010 1011 return arr.Nelems, nil 1012 } 1013 1014 // resolveBTFArrayMacro resolves the __array macro, which declares an array 1015 // of pointers to a given type. This function returns the target Type of 1016 // the pointers in the array. 1017 func resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) { 1018 arr, ok := typ.(*btf.Array) 1019 if !ok { 1020 return nil, fmt.Errorf("not an array: %v", typ) 1021 } 1022 1023 ptr, ok := arr.Type.(*btf.Pointer) 1024 if !ok { 1025 return nil, fmt.Errorf("not an array of pointers: %v", typ) 1026 } 1027 1028 return ptr.Target, nil 1029 } 1030 1031 // resolveBTFValuesContents resolves relocations into ELF sections belonging 1032 // to btf.VarSecinfo's. This can be used on the 'values' member in BTF map 1033 // definitions to extract static declarations of map contents. 1034 func resolveBTFValuesContents(es *elfSection, vs *btf.VarSecinfo, member btf.Member) ([]MapKV, error) { 1035 // The elements of a .values pointer array are not encoded in BTF. 1036 // Instead, relocations are generated into each array index. 1037 // However, it's possible to leave certain array indices empty, so all 1038 // indices' offsets need to be checked for emitted relocations. 1039 1040 // The offset of the 'values' member within the _struct_ (in bits) 1041 // is the starting point of the array. Convert to bytes. Add VarSecinfo 1042 // offset to get the absolute position in the ELF blob. 1043 start := member.Offset.Bytes() + vs.Offset 1044 // 'values' is encoded in BTF as a zero (variable) length struct 1045 // member, and its contents run until the end of the VarSecinfo. 1046 // Add VarSecinfo offset to get the absolute position in the ELF blob. 1047 end := vs.Size + vs.Offset 1048 // The size of an address in this section. This determines the width of 1049 // an index in the array. 1050 align := uint32(es.SectionHeader.Addralign) 1051 1052 // Check if variable-length section is aligned. 1053 if (end-start)%align != 0 { 1054 return nil, errors.New("unaligned static values section") 1055 } 1056 elems := (end - start) / align 1057 1058 if elems == 0 { 1059 return nil, nil 1060 } 1061 1062 contents := make([]MapKV, 0, elems) 1063 1064 // k is the array index, off is its corresponding ELF section offset. 1065 for k, off := uint32(0), start; k < elems; k, off = k+1, off+align { 1066 r, ok := es.relocations[uint64(off)] 1067 if !ok { 1068 continue 1069 } 1070 1071 // Relocation exists for the current offset in the ELF section. 1072 // Emit a value stub based on the type of relocation to be replaced by 1073 // a real fd later in the pipeline before populating the map. 1074 // Map keys are encoded in MapKV entries, so empty array indices are 1075 // skipped here. 1076 switch t := elf.ST_TYPE(r.Info); t { 1077 case elf.STT_FUNC: 1078 contents = append(contents, MapKV{uint32(k), r.Name}) 1079 case elf.STT_OBJECT: 1080 contents = append(contents, MapKV{uint32(k), r.Name}) 1081 default: 1082 return nil, fmt.Errorf("unknown relocation type %v for symbol %s", t, r.Name) 1083 } 1084 } 1085 1086 return contents, nil 1087 } 1088 1089 func (ec *elfCode) loadDataSections() error { 1090 for _, sec := range ec.sections { 1091 if sec.kind != dataSection { 1092 continue 1093 } 1094 1095 if sec.references == 0 { 1096 // Prune data sections which are not referenced by any 1097 // instructions. 1098 continue 1099 } 1100 1101 mapSpec := &MapSpec{ 1102 Name: SanitizeName(sec.Name, -1), 1103 Type: Array, 1104 KeySize: 4, 1105 ValueSize: uint32(sec.Size), 1106 MaxEntries: 1, 1107 } 1108 1109 switch sec.Type { 1110 // Only open the section if we know there's actual data to be read. 1111 case elf.SHT_PROGBITS: 1112 data, err := sec.Data() 1113 if err != nil { 1114 return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err) 1115 } 1116 1117 if uint64(len(data)) > math.MaxUint32 { 1118 return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name) 1119 } 1120 mapSpec.Contents = []MapKV{{uint32(0), data}} 1121 1122 case elf.SHT_NOBITS: 1123 // NOBITS sections like .bss contain only zeroes, and since data sections 1124 // are Arrays, the kernel already preallocates them. Skip reading zeroes 1125 // from the ELF. 1126 default: 1127 return fmt.Errorf("data section %s: unknown section type %s", sec.Name, sec.Type) 1128 } 1129 1130 // It is possible for a data section to exist without a corresponding BTF Datasec 1131 // if it only contains anonymous values like macro-defined arrays. 1132 if ec.btf != nil { 1133 var ds *btf.Datasec 1134 if ec.btf.TypeByName(sec.Name, &ds) == nil { 1135 // Assign the spec's key and BTF only if the Datasec lookup was successful. 1136 mapSpec.Key = &btf.Void{} 1137 mapSpec.Value = ds 1138 } 1139 } 1140 1141 if strings.HasPrefix(sec.Name, ".rodata") { 1142 mapSpec.Flags = unix.BPF_F_RDONLY_PROG 1143 mapSpec.Freeze = true 1144 } 1145 1146 ec.maps[sec.Name] = mapSpec 1147 } 1148 1149 return nil 1150 } 1151 1152 // loadKconfigSection handles the 'virtual' Datasec .kconfig that doesn't 1153 // have a corresponding ELF section and exist purely in BTF. 1154 func (ec *elfCode) loadKconfigSection() error { 1155 if ec.btf == nil { 1156 return nil 1157 } 1158 1159 var ds *btf.Datasec 1160 err := ec.btf.TypeByName(".kconfig", &ds) 1161 if errors.Is(err, btf.ErrNotFound) { 1162 return nil 1163 } 1164 if err != nil { 1165 return err 1166 } 1167 1168 if ds.Size == 0 { 1169 return errors.New("zero-length .kconfig") 1170 } 1171 1172 ec.kconfig = &MapSpec{ 1173 Name: ".kconfig", 1174 Type: Array, 1175 KeySize: uint32(4), 1176 ValueSize: ds.Size, 1177 MaxEntries: 1, 1178 Flags: unix.BPF_F_RDONLY_PROG, 1179 Freeze: true, 1180 Key: &btf.Int{Size: 4}, 1181 Value: ds, 1182 } 1183 1184 return nil 1185 } 1186 1187 // loadKsymsSection handles the 'virtual' Datasec .ksyms that doesn't 1188 // have a corresponding ELF section and exist purely in BTF. 1189 func (ec *elfCode) loadKsymsSection() error { 1190 if ec.btf == nil { 1191 return nil 1192 } 1193 1194 var ds *btf.Datasec 1195 err := ec.btf.TypeByName(".ksyms", &ds) 1196 if errors.Is(err, btf.ErrNotFound) { 1197 return nil 1198 } 1199 if err != nil { 1200 return err 1201 } 1202 1203 for _, v := range ds.Vars { 1204 // we have already checked the .ksyms Datasec to only contain Func Vars. 1205 ec.kfuncs[v.Type.TypeName()] = v.Type.(*btf.Func) 1206 } 1207 1208 return nil 1209 } 1210 1211 type libbpfElfSectionDef struct { 1212 pattern string 1213 programType sys.ProgType 1214 attachType sys.AttachType 1215 flags libbpfElfSectionFlag 1216 } 1217 1218 type libbpfElfSectionFlag uint32 1219 1220 // The values correspond to enum sec_def_flags in libbpf. 1221 const ( 1222 _SEC_NONE libbpfElfSectionFlag = 0 1223 1224 _SEC_EXP_ATTACH_OPT libbpfElfSectionFlag = 1 << (iota - 1) 1225 _SEC_ATTACHABLE 1226 _SEC_ATTACH_BTF 1227 _SEC_SLEEPABLE 1228 _SEC_XDP_FRAGS 1229 _SEC_USDT 1230 1231 // Ignore any present extra in order to preserve backwards compatibility 1232 // with earlier versions of the library. 1233 ignoreExtra 1234 1235 _SEC_ATTACHABLE_OPT = _SEC_ATTACHABLE | _SEC_EXP_ATTACH_OPT 1236 ) 1237 1238 func init() { 1239 // Compatibility with older versions of the library. 1240 // We prepend libbpf definitions since they contain a prefix match 1241 // for "xdp". 1242 elfSectionDefs = append([]libbpfElfSectionDef{ 1243 {"xdp.frags/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP, _SEC_XDP_FRAGS | ignoreExtra}, 1244 {"xdp.frags_devmap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, _SEC_XDP_FRAGS}, 1245 {"xdp_devmap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, 0}, 1246 {"xdp.frags_cpumap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, _SEC_XDP_FRAGS}, 1247 {"xdp_cpumap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, 0}, 1248 // This has been in the library since the beginning of time. Not sure 1249 // where it came from. 1250 {"seccomp", sys.BPF_PROG_TYPE_SOCKET_FILTER, 0, _SEC_NONE}, 1251 }, elfSectionDefs...) 1252 } 1253 1254 func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) { 1255 // Skip optional program marking for now. 1256 sectionName = strings.TrimPrefix(sectionName, "?") 1257 1258 for _, t := range elfSectionDefs { 1259 extra, ok := matchSectionName(sectionName, t.pattern) 1260 if !ok { 1261 continue 1262 } 1263 1264 programType := ProgramType(t.programType) 1265 attachType := AttachType(t.attachType) 1266 1267 var flags uint32 1268 if t.flags&_SEC_SLEEPABLE > 0 { 1269 flags |= unix.BPF_F_SLEEPABLE 1270 } 1271 if t.flags&_SEC_XDP_FRAGS > 0 { 1272 flags |= unix.BPF_F_XDP_HAS_FRAGS 1273 } 1274 if t.flags&_SEC_EXP_ATTACH_OPT > 0 { 1275 if programType == XDP { 1276 // The library doesn't yet have code to fallback to not specifying 1277 // attach type. Only do this for XDP since we've enforced correct 1278 // attach type for all other program types. 1279 attachType = AttachNone 1280 } 1281 } 1282 if t.flags&ignoreExtra > 0 { 1283 extra = "" 1284 } 1285 1286 return programType, attachType, flags, extra 1287 } 1288 1289 return UnspecifiedProgram, AttachNone, 0, "" 1290 } 1291 1292 // matchSectionName checks a section name against a pattern. 1293 // 1294 // It's behaviour mirrors that of libbpf's sec_def_matches. 1295 func matchSectionName(sectionName, pattern string) (extra string, found bool) { 1296 have, extra, found := strings.Cut(sectionName, "/") 1297 want := strings.TrimRight(pattern, "+/") 1298 1299 if strings.HasSuffix(pattern, "/") { 1300 // Section name must have a slash and extra may be empty. 1301 return extra, have == want && found 1302 } else if strings.HasSuffix(pattern, "+") { 1303 // Section name may have a slash and extra may be empty. 1304 return extra, have == want 1305 } 1306 1307 // Section name must have a prefix. extra is ignored. 1308 return "", strings.HasPrefix(sectionName, pattern) 1309 } 1310 1311 func (ec *elfCode) loadSectionRelocations(sec *elf.Section, symbols []elf.Symbol) (map[uint64]elf.Symbol, error) { 1312 rels := make(map[uint64]elf.Symbol) 1313 1314 if sec.Entsize < 16 { 1315 return nil, fmt.Errorf("section %s: relocations are less than 16 bytes", sec.Name) 1316 } 1317 1318 r := bufio.NewReader(sec.Open()) 1319 for off := uint64(0); off < sec.Size; off += sec.Entsize { 1320 ent := io.LimitReader(r, int64(sec.Entsize)) 1321 1322 var rel elf.Rel64 1323 if binary.Read(ent, ec.ByteOrder, &rel) != nil { 1324 return nil, fmt.Errorf("can't parse relocation at offset %v", off) 1325 } 1326 1327 symNo := int(elf.R_SYM64(rel.Info) - 1) 1328 if symNo >= len(symbols) { 1329 return nil, fmt.Errorf("offset %d: symbol %d doesn't exist", off, symNo) 1330 } 1331 1332 symbol := symbols[symNo] 1333 rels[rel.Off] = symbol 1334 } 1335 1336 return rels, nil 1337 }