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