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