github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/elf.go (about) 1 package gobpfld 2 3 import ( 4 "debug/elf" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "io" 9 "strconv" 10 "strings" 11 12 "github.com/dylandreimerink/gobpfld/bpftypes" 13 "github.com/dylandreimerink/gobpfld/ebpf" 14 "github.com/dylandreimerink/gobpfld/internal/cstr" 15 "github.com/dylandreimerink/gobpfld/perf" 16 ) 17 18 type ELFParseSettings struct { 19 // If true, names which are to large will be truncated, this can cause unexpected behavior 20 // Otherwise an error will be generated. 21 TruncateNames bool 22 } 23 24 // BPFELF is the result of parsing an eBPF ELF file. It can contain multiple programs and maps. 25 type BPFELF struct { 26 ByteOrder binary.ByteOrder 27 // Programs contained within the ELF 28 Programs map[string]BPFProgram 29 // Maps defined in the ELF 30 Maps map[string]BPFMap 31 // BTF contains type and debugging information regarding the programs and maps. 32 BTF *BTF 33 } 34 35 // bpfELF is a temporary structure which we used to store data across multiple stages of parsing the ELF file. 36 type bpfELF struct { 37 ByteOrder binary.ByteOrder 38 // ElfPrograms contained within the ELF 39 ElfPrograms map[string]*elfBPFProgram 40 Programs map[string]BPFProgram 41 // AbstractMaps defined in the ELF 42 AbstractMaps map[string]AbstractMap 43 Maps map[string]BPFMap 44 // BTF contains type and debugging information regarding the programs and maps. 45 BTF *BTF 46 47 // eBPF code found in the .text section, often called "sub programs". 48 // Used for library code and code shared by multiple programs by way of BPF to BPF calls 49 txtInstr []ebpf.RawInstruction 50 // A list of relocation tables by section name 51 relTables map[string]elfRelocTable 52 53 // Store the data of the .BTF.ext section, since we need to parse it after 54 // the .BTF section. 55 btfExtBytes []byte 56 57 settings ELFParseSettings 58 elfFile *elf.File 59 license string 60 } 61 62 type elfBPFProgram struct { 63 AbstractBPFProgram 64 65 // The ELF section where the program came from 66 section string 67 // The offset from the start of the section to the start of the program in bytes. 68 offset int 69 // The size of the program code in bytes, which is not always the size of the instructions slice 70 // due to bpf_to_bpf linking 71 size int 72 } 73 74 func LoadProgramFromELF(r io.ReaderAt, settings ELFParseSettings) (BPFELF, error) { 75 elfFile, err := elf.NewFile(r) 76 if err != nil { 77 return BPFELF{}, err 78 } 79 80 if elfFile.Machine != elf.EM_BPF { 81 return BPFELF{}, fmt.Errorf("elf file machine type is not BPF, machine type: '%s'", elfFile.Machine) 82 } 83 84 if elfFile.Class != elf.ELFCLASS64 { 85 return BPFELF{}, fmt.Errorf("elf file class is not 64 bit, class: '%s'", elfFile.Class) 86 } 87 88 bpfElf := newBPFELF(elfFile, settings) 89 90 err = bpfElf.parseElf() 91 if err != nil { 92 return BPFELF{}, fmt.Errorf("parse ELF: %w", err) 93 } 94 95 err = bpfElf.processBTF() 96 if err != nil { 97 return BPFELF{}, fmt.Errorf("process BTF: %w", err) 98 } 99 100 err = bpfElf.linkAndRelocate() 101 if err != nil { 102 return BPFELF{}, fmt.Errorf("link and relocate: %w", err) 103 } 104 105 err = bpfElf.specializePrograms() 106 if err != nil { 107 return BPFELF{}, fmt.Errorf("link and relocate: %w", err) 108 } 109 110 return bpfElf.toBPFELF(), nil 111 } 112 113 func newBPFELF(elfFile *elf.File, settings ELFParseSettings) *bpfELF { 114 return &bpfELF{ 115 ByteOrder: elfFile.ByteOrder, 116 ElfPrograms: make(map[string]*elfBPFProgram), 117 Programs: make(map[string]BPFProgram), 118 AbstractMaps: make(map[string]AbstractMap), 119 Maps: make(map[string]BPFMap), 120 relTables: make(map[string]elfRelocTable), 121 122 settings: settings, 123 elfFile: elfFile, 124 license: "Unknown", 125 } 126 } 127 128 // Parse the ELF file into the separate components which will need to be combined later 129 func (bpfElf *bpfELF) parseElf() error { 130 for sectionIndex, section := range bpfElf.elfFile.Sections { 131 // TODO instead of making an exception for .bss, handle all datasections differently 132 if section.Type == elf.SHT_PROGBITS || section.Name == ".bss" { 133 err := bpfElf.parseProgBits(sectionIndex, section) 134 if err != nil { 135 return fmt.Errorf("parse prog bits: %w", err) 136 } 137 138 continue 139 } 140 141 if section.Type == elf.SHT_REL { 142 err := bpfElf.parseRelocationTables(section) 143 if err != nil { 144 return fmt.Errorf("parse relocation tables: %w", err) 145 } 146 147 continue 148 } 149 } 150 151 return nil 152 } 153 154 // processBTF parses extended BTF information and patches DataSec entries 155 func (bpfElf *bpfELF) processBTF() error { 156 // If set, parse the .BTF.ext section now, since we have to guarantee it happens after 157 // BTF parsing since the Ext uses the string table and types from the main section. 158 if bpfElf.btfExtBytes != nil { 159 if bpfElf.BTF == nil { 160 bpfElf.BTF = NewBTF() 161 } 162 163 err := bpfElf.BTF.ParseBTFExt(bpfElf.btfExtBytes) 164 if err != nil { 165 return fmt.Errorf("parse .BTF.ext: %w", err) 166 } 167 } 168 169 symbols, err := bpfElf.elfFile.Symbols() 170 if err != nil { 171 return fmt.Errorf("get symbols: %w", err) 172 } 173 174 // Patch total Size of DataSec types, and offsets for the individual variables 175 for _, btfType := range bpfElf.BTF.Types { 176 dataSec, ok := btfType.(*BTFDataSecType) 177 if !ok { 178 continue 179 } 180 181 section := bpfElf.elfFile.Section(dataSec.Name) 182 if section != nil { 183 dataSec.Size = uint32(section.Size) 184 185 for i, variable := range dataSec.Variables { 186 for _, sym := range symbols { 187 // Ignore any symbols which are not for the current section 188 if len(bpfElf.elfFile.Sections) <= int(sym.Section) || 189 bpfElf.elfFile.Sections[sym.Section] != section { 190 continue 191 } 192 193 // The symbols name must match the name of the DataSec variable type 194 if sym.Name != variable.Type.GetName() { 195 continue 196 } 197 198 // The value of the symbol is the offset from the start of the section 199 dataSec.Variables[i].Offset = uint32(sym.Value) 200 201 break 202 } 203 } 204 } 205 } 206 207 // If we have a BTF object, add it to all programs 208 if bpfElf.BTF != nil { 209 for _, program := range bpfElf.ElfPrograms { 210 program.BTF = bpfElf.BTF 211 } 212 } 213 214 return nil 215 } 216 217 // parseRelocationTables converts the binary relocation tables into Go types 218 func (bpfElf *bpfELF) parseRelocationTables(section *elf.Section) error { 219 data, err := section.Data() 220 if err != nil { 221 return fmt.Errorf("error while loading section '%s': %w", section.Name, err) 222 } 223 224 if len(data)%16 != 0 { 225 return fmt.Errorf("size of relocation table '%s' not devisable by 16", section.Name) 226 } 227 228 symbols, err := bpfElf.elfFile.Symbols() 229 if err != nil { 230 return fmt.Errorf("get symbols: %w", err) 231 } 232 233 relTable := make(elfRelocTable, len(data)/16) 234 for i := 0; i < len(data); i += 16 { 235 entry := elfRelocEntry{ 236 Rel64: elf.Rel64{ 237 Off: bpfElf.elfFile.ByteOrder.Uint64(data[i : i+8]), 238 Info: bpfElf.elfFile.ByteOrder.Uint64(data[i+8 : i+16]), 239 }, 240 } 241 symNum := elf.R_SYM64(entry.Info) 242 if uint32(len(symbols)) < symNum { 243 return fmt.Errorf( 244 "symbol number in relocation table '%s' does not exist in symbol table", 245 section.Name, 246 ) 247 } 248 249 entry.Symbol = &symbols[symNum-1] 250 entry.Type = elf_r_bpf(elf.R_TYPE64(entry.Info)) 251 252 relTable[i/16] = entry 253 } 254 255 bpfElf.relTables[section.Name] = relTable 256 257 return nil 258 } 259 260 // parseProgBits parses ELF sections of type progbits, which can contain a number of different types of data 261 // depending on the name of the section. 262 func (bpfElf *bpfELF) parseProgBits(sectionIndex int, section *elf.Section) error { 263 data, err := section.Data() 264 if err != nil { 265 return fmt.Errorf("error while loading section '%s': %w", section.Name, err) 266 } 267 268 switch section.Name { 269 case "license": 270 bpfElf.license = cstr.BytesToString(data) 271 272 case "maps", ".maps": 273 err := bpfElf.parseMaps(sectionIndex, section) 274 if err != nil { 275 return fmt.Errorf("parse maps: %w", err) 276 } 277 278 case ".data", ".rodata", ".bss": 279 err := bpfElf.dataToMap(sectionIndex, section) 280 if err != nil { 281 return fmt.Errorf("data to map: %w", err) 282 } 283 284 case ".BTF": 285 // BTF type and string information 286 287 if bpfElf.BTF == nil { 288 bpfElf.BTF = NewBTF() 289 } 290 291 err := bpfElf.BTF.ParseBTF(data) 292 if err != nil { 293 return fmt.Errorf("parse .BTF: %w", err) 294 } 295 296 case ".BTF.ext": 297 // BTF line and function information 298 299 // Just save the data, we have to process it later to guarantee it happens after .BTF parsing 300 bpfElf.btfExtBytes = data 301 302 // TODO parse .BTF_ids (Used to identify specific types in the kernel for tracing etc.) 303 default: 304 // Assume program 305 err := bpfElf.parseProgram(sectionIndex, section) 306 if err != nil { 307 return fmt.Errorf("parse program: %w", err) 308 } 309 } 310 311 return nil 312 } 313 314 // parseProgram parses an ELF section as an BPF program 315 func (bpfElf *bpfELF) parseProgram(sectionIndex int, section *elf.Section) error { 316 // If the section flags don't indicate it contains instructions, it not a program 317 if section.Flags&elf.SHF_EXECINSTR == 0 { 318 return nil 319 } 320 321 data, err := section.Data() 322 if err != nil { 323 return fmt.Errorf("error while loading section '%s': %w", section.Name, err) 324 } 325 326 if len(data)%ebpf.BPFInstSize != 0 { 327 return fmt.Errorf("elf section is incorrect size for BPF program, should be divisible by 8") 328 } 329 330 instructions := make([]ebpf.RawInstruction, len(data)/ebpf.BPFInstSize) 331 for i := 0; i < len(data); i += ebpf.BPFInstSize { 332 instructions[i/ebpf.BPFInstSize] = ebpf.RawInstruction{ 333 Op: data[i], 334 Reg: data[i+1], 335 Off: int16(bpfElf.elfFile.ByteOrder.Uint16(data[i+2 : i+4])), 336 Imm: int32(bpfElf.elfFile.ByteOrder.Uint32(data[i+4 : i+8])), 337 } 338 } 339 340 // If this is the .text section, save the instructions in a separate slice without a program struct 341 if section.Name == ".text" { 342 bpfElf.txtInstr = instructions 343 return nil 344 } 345 346 // For other sections, create it as a separate program 347 348 sectionParts := strings.Split(section.Name, "/") 349 progType := sectionNameToProgType[sectionParts[0]] 350 351 if progType == bpftypes.BPF_PROG_TYPE_UNSPEC { 352 return fmt.Errorf( 353 "unknown elf section '%s', doesn't match any eBPF program type", 354 sectionParts[0], 355 ) 356 } 357 358 symbols, err := bpfElf.elfFile.Symbols() 359 if err != nil { 360 return fmt.Errorf("get symbols: %w", err) 361 } 362 363 // Loop over all symbols for this section 364 for _, sym := range symbols { 365 if sym.Section != elf.SectionIndex(sectionIndex) { 366 continue 367 } 368 369 // BPF programs appear as global functions in the ELF file 370 if elf.ST_BIND(sym.Info) != elf.STB_GLOBAL || elf.ST_TYPE(sym.Info) != elf.STT_FUNC { 371 continue 372 } 373 374 program := NewAbstractBPFProgram() 375 program.ProgramType = progType 376 start := int(sym.Value) / ebpf.BPFInstSize 377 end := (int(sym.Value) + int(sym.Size)) / ebpf.BPFInstSize 378 program.Instructions = instructions[start:end] 379 380 err = program.Name.SetString(sym.Name) 381 if err != nil { 382 if bpfElf.settings.TruncateNames && errors.Is(err, ErrObjNameToLarge) { 383 err = program.Name.SetString(sym.Name[:bpftypes.BPF_OBJ_NAME_LEN-1]) 384 if err != nil { 385 return fmt.Errorf("failed to truncate program name: %w", err) 386 } 387 } else { 388 return fmt.Errorf("failed to set program name '%s': %w", sym.Name, err) 389 } 390 } 391 392 bpfElf.ElfPrograms[sym.Name] = &elfBPFProgram{ 393 AbstractBPFProgram: program, 394 section: section.Name, 395 offset: int(sym.Value), 396 size: int(sym.Size), 397 } 398 } 399 400 return nil 401 } 402 403 // The .data, .rodata, and .rss sections are loaded as maps with a single value containing the data blob 404 // of the ELF section. 405 func (bpfElf *bpfELF) dataToMap(sectionIndex int, section *elf.Section) error { 406 secData, err := section.Data() 407 if err != nil { 408 return fmt.Errorf("get section data: %w", err) 409 } 410 411 datasecMap := &dataMap{ 412 AbstractMap: AbstractMap{ 413 Definition: BPFMapDef{ 414 Type: bpftypes.BPF_MAP_TYPE_ARRAY, 415 KeySize: 4, 416 ValueSize: uint32(len(secData)), 417 MaxEntries: 1, 418 }, 419 }, 420 } 421 422 datasecMap.InitialData = map[interface{}]interface{}{ 423 0: secData, 424 } 425 426 switch section.Name { 427 case ".rodata": 428 datasecMap.readOnly = true 429 datasecMap.Name = MustNewObjName("rodata") 430 case ".bss": 431 datasecMap.Name = MustNewObjName("bss") 432 // .bss is zero initialzed, the kernel will zero-init the map, so if we just don't write 433 // to it, we are good. 434 datasecMap.InitialData = nil 435 case ".data": 436 datasecMap.Name = MustNewObjName("data") 437 } 438 439 bpfElf.Maps[datasecMap.Name.String()] = datasecMap 440 441 return nil 442 } 443 444 // parseMaps parses an ELF section as containing map data 445 func (bpfElf *bpfELF) parseMaps(sectionIndex int, section *elf.Section) error { 446 data, err := section.Data() 447 if err != nil { 448 return fmt.Errorf("error while loading section '%s': %w", section.Name, err) 449 } 450 451 // If section flag does not have alloc, its not a proper map def 452 if section.Flags&elf.SHF_ALLOC == 0 { 453 return errors.New("maps section has no ALLOC flag") 454 } 455 456 symbols, err := bpfElf.elfFile.Symbols() 457 if err != nil { 458 return fmt.Errorf("get symbols: %w", err) 459 } 460 461 for i := 0; i < len(data); i += bpfMapDefSize { 462 abstractMap := AbstractMap{ 463 Definition: BPFMapDef{ 464 Type: bpftypes.BPFMapType(bpfElf.elfFile.ByteOrder.Uint32(data[i : i+4])), 465 KeySize: bpfElf.elfFile.ByteOrder.Uint32(data[i+4 : i+8]), 466 ValueSize: bpfElf.elfFile.ByteOrder.Uint32(data[i+8 : i+12]), 467 MaxEntries: bpfElf.elfFile.ByteOrder.Uint32(data[i+12 : i+16]), 468 Flags: bpftypes.BPFMapFlags(bpfElf.elfFile.ByteOrder.Uint32(data[i+16 : i+20])), 469 }, 470 } 471 472 longName := "" 473 for _, symbol := range symbols { 474 // If the symbol isn't for this section 475 if int(symbol.Section) != sectionIndex { 476 continue 477 } 478 479 // if the symbol is not for the current data offset 480 if symbol.Value != uint64(i) { 481 continue 482 } 483 484 longName = symbol.Name 485 err = abstractMap.Name.SetString(symbol.Name) 486 if err != nil { 487 if bpfElf.settings.TruncateNames && errors.Is(err, ErrObjNameToLarge) { 488 err = abstractMap.Name.SetString(symbol.Name[:bpftypes.BPF_OBJ_NAME_LEN-1]) 489 if err != nil { 490 return fmt.Errorf("failed to truncate map name: %w", err) 491 } 492 } else { 493 return fmt.Errorf("failed to set map name: %w", err) 494 } 495 } 496 497 break 498 } 499 500 if abstractMap.Name.String() == "" { 501 return fmt.Errorf( 502 "unable to find name in symbol table for map at index %d in section '%s'", 503 i, 504 section.Name, 505 ) 506 } 507 508 // TODO map name duplicate check 509 510 bpfElf.AbstractMaps[longName] = abstractMap 511 } 512 513 return nil 514 } 515 516 // linkAndRelocate links data parsed from different ELF sections together and relocates any addresses/pointers 517 // that have changed as result of the linking. 518 func (bpfElf *bpfELF) linkAndRelocate() error { 519 // Set BTF k/v type for dataMaps 520 for name, bpfMap := range bpfElf.Maps { 521 dataMap, ok := bpfMap.(*dataMap) 522 if !ok { 523 continue 524 } 525 526 dataMap.BTF = bpfElf.BTF 527 if bpfElf.BTF != nil { 528 dataSec := bpfElf.BTF.typesByName["."+name] 529 dataMap.BTFMapType = BTFMap{ 530 Key: &BTFVoidType{}, 531 Value: dataSec, 532 } 533 } 534 bpfElf.Maps[name] = dataMap 535 } 536 537 // Add BTF info to the abstract maps and resolve the actual map type, to be used during map loading. 538 for name, bpfMap := range bpfElf.AbstractMaps { 539 bpfMap.BTF = bpfElf.BTF 540 if bpfElf.BTF != nil { 541 func() { 542 // TODO resolve more information about the map from BTF 543 // bpfMap.BTFMapType = bpfElf.BTF.typesByName[name] 544 // } 545 546 // The "new" way of doing it is to allow users to specify a 'key' and 'value' field instread of the 547 // sizes.These fields should be pointers to the types used in the key and or value types. 548 // We can use the BTF types to infer the size of these types for the map_def. 549 // https://lwn.net/ml/netdev/20190531202132.379386-7-andriin@fb.com/ 550 // We can't use this method yet until the map parsing in an earlier phase can postpone map creation 551 // until now. So use the "old school" method instread :( . 552 553 // The "old school" way of associating BTF Key/Value types to a map is with an annotation which no 554 // longer exists(BPF_ANNOTATE_KV_PAIR). This would produce an additional struct ____btf_map_##name 555 // which refers to the key and value types. 556 // https://github.com/libbpf/libbpf/blob/3febb8a16597f4605450f6c947f4deefb446cb8a/src/btf.c#L1401 557 558 container, found := bpfElf.BTF.typesByName["____btf_map_"+name] 559 if !found { 560 return 561 } 562 563 annotationVar, ok := container.(*BTFVarType) 564 if !ok { 565 return 566 } 567 568 annotationType, ok := annotationVar.Type.(*BTFStructType) 569 if !ok { 570 return 571 } 572 573 for _, m := range annotationType.Members { 574 if m.Name == "key" { 575 bpfMap.BTFMapType.Key = m.Type 576 } 577 if m.Name == "value" { 578 bpfMap.BTFMapType.Value = m.Type 579 } 580 } 581 }() 582 } 583 584 bpfElf.Maps[name] = bpfMapFromAbstractMap(bpfMap) 585 } 586 587 // Index lines and funcs by section since we will be relocating per section 588 btfLinesPerSection := make(map[string][]BTFLine) 589 btfFuncsPerSection := make(map[string][]BTFFunc) 590 if bpfElf.BTF != nil { 591 for _, line := range bpfElf.BTF.Lines { 592 lines := btfLinesPerSection[line.Section] 593 lines = append(lines, line) 594 btfLinesPerSection[line.Section] = lines 595 } 596 for _, f := range bpfElf.BTF.Funcs { 597 funcs := btfFuncsPerSection[f.Section] 598 funcs = append(funcs, f) 599 btfFuncsPerSection[f.Section] = funcs 600 } 601 } 602 603 for _, program := range bpfElf.ElfPrograms { 604 progRelocTable, found := bpfElf.relTables[".rel"+program.section] 605 if !found { 606 continue 607 } 608 609 // The offset where main program instructions ends and .text instructions start. 610 txtInsOff := -1 611 612 // If there is any code in the .text section 613 if len(bpfElf.txtInstr) > 0 { 614 // Check if this program uses any code from the .text section. 615 // If there are multiple programs, not all may need the .text code. 616 // Adding it causes the verifier to throw dead code errors. 617 // 618 // TODO look into only adding the sub programs needed, not the whole .text block (if doable) 619 // if there is dead-code the verifier will refuse to load, so this might be a good feature. 620 usesTxt := false 621 for _, relocEntry := range progRelocTable { 622 // The absolute offset from the start of the section to the location where the entry should be linked 623 absOff, err := relocEntry.AbsoluteOffset() 624 if err != nil { 625 return fmt.Errorf("unable to calculate absolute offset for relocation entry: %w", err) 626 } 627 628 // Calculate the offset from the start of the program within a section 629 progOff := int(absOff) - program.offset 630 631 // If the relocation entry is for before or after a current program (another program in the same 632 // section). Ignore it. 633 if progOff < 0 || progOff >= program.size { 634 continue 635 } 636 637 if bpfElf.elfFile.Sections[int(relocEntry.Symbol.Section)].Name == ".text" { 638 usesTxt = true 639 break 640 } 641 } 642 643 if usesTxt { 644 // Make a new instructions slice which can hold the main instruction and .text instructions 645 newInst := make([]ebpf.RawInstruction, len(program.Instructions)+len(bpfElf.txtInstr)) 646 txtInsOff = copy(newInst, program.Instructions) 647 copy(newInst[txtInsOff:], bpfElf.txtInstr) 648 program.Instructions = newInst 649 650 // Add the BTF lines of the .text section to the ones of the program section 651 progLines := btfLinesPerSection[program.section] 652 for _, textLine := range btfLinesPerSection[".text"] { 653 progLines = append(progLines, BTFLine{ 654 Section: program.section, 655 SectionOffset: progLines[0].SectionOffset, 656 InstructionOffset: textLine.InstructionOffset + uint32(txtInsOff*ebpf.BPFInstSize), 657 FileName: textLine.FileName, 658 FileNameOffset: textLine.FileNameOffset, 659 Line: textLine.Line, 660 LineOffset: textLine.LineOffset, 661 LineNumber: textLine.LineNumber, 662 ColumnNumber: textLine.ColumnNumber, 663 }) 664 } 665 btfLinesPerSection[program.section] = progLines 666 667 progFuncs := btfFuncsPerSection[program.section] 668 for _, progFunc := range btfFuncsPerSection[".text"] { 669 progFuncs = append(progFuncs, BTFFunc{ 670 Section: program.section, 671 SectionOffset: progLines[0].SectionOffset, 672 InstructionOffset: progFunc.InstructionOffset + uint32(txtInsOff*ebpf.BPFInstSize), 673 Type: progFunc.Type, 674 TypeID: progFunc.TypeID, 675 }) 676 } 677 btfFuncsPerSection[program.section] = progFuncs 678 } 679 } 680 681 for _, progLines := range btfLinesPerSection[program.section] { 682 line := progLines.ToKernel() 683 // The offsets in ELF are in bytes from section start, for the kernel we need the offset of instructions 684 // Since the program is at the top, we can just divide by the instruction size. 685 line.InstructionOffset = line.InstructionOffset / uint32(ebpf.BPFInstSize) 686 program.BTFLines = append(program.BTFLines, line) 687 } 688 for _, progFuncs := range btfFuncsPerSection[program.section] { 689 f := progFuncs.ToKernel() 690 // The offsets in ELF are in bytes from section start, for the kernel we need the offset of instructions 691 // Since the program is at the top, we can just divide by the instruction size. 692 f.InstructionOffset = f.InstructionOffset / uint32(ebpf.BPFInstSize) 693 program.BTFFuncs = append(program.BTFFuncs, f) 694 } 695 696 if bpfElf.BTF != nil { 697 // Each program is only interested in lines and funcs applicable to itself, not other programs in the 698 // ELF file. So copy the BTF struct, this will keep pointers to types etc. 699 progBTF := *bpfElf.BTF 700 701 // Just replace the lines and funcs slices in this copy 702 progBTF.Lines = btfLinesPerSection[program.section] 703 progBTF.Funcs = btfFuncsPerSection[program.section] 704 705 program.BTF = &progBTF 706 } 707 708 // Handle relocation entries which can includes: 709 // - Map references(need to be resolved at load time) 710 // - BPF to BPF function calls (can be resolved here) 711 // - Global data (.data, .bss, .rodata) 712 for _, relocEntry := range progRelocTable { 713 section := bpfElf.elfFile.Sections[relocEntry.Symbol.Section] 714 715 // The absolute offset from the start of the section to the location where the entry should be linked 716 absOff, err := relocEntry.AbsoluteOffset() 717 if err != nil { 718 return fmt.Errorf("unable to calculate absolute offset for relocation entry: %w", err) 719 } 720 721 // Calculate the offset from the start of the program within a section 722 progOff := int(absOff) - program.offset 723 724 // If the relocation entry is for before or after a current program (another program in the same section) 725 // Ignore it. 726 if progOff < 0 || progOff >= program.size { 727 continue 728 } 729 730 if section.Name == ".text" { 731 if txtInsOff == -1 { 732 return fmt.Errorf("unable to relocate .text entry since it is empty") 733 } 734 735 // Update the imm of the call instruction which points to a relocated function 736 // in the .text section to reflect the current relative offset 737 callInst := &program.Instructions[progOff/ebpf.BPFInstSize] 738 callInst.Imm = (int32(txtInsOff) + callInst.Imm) - (int32(progOff) / int32(ebpf.BPFInstSize)) 739 740 continue 741 } 742 743 globalData := section.Name == ".data" || section.Name == ".rodata" || section.Name == ".bss" 744 745 // TODO maps and .maps should be handled differently. The maps section always contains "old school" 20byte 746 // map definitions. The .maps section, contains BTF-defined maps, the map definition for these maps should 747 // be created based on both the info in the section as well as BTF info. 748 // https://lwn.net/ml/netdev/20190531202132.379386-7-andriin@fb.com/ 749 if section.Name == "maps" || section.Name == ".maps" || globalData { 750 // The map name is the name of the symbol truncated to BPF_OBJ_NAME_LEN 751 mapName := relocEntry.Symbol.Name 752 753 // the dot of the .data, .rodata, and .bss sections are removed 754 if globalData { 755 mapName = section.Name[1:] 756 757 firstInst := &program.Instructions[progOff/ebpf.BPFInstSize] 758 firstInst.SetSourceReg(ebpf.BPF_PSEUDO_MAP_FD_VALUE) 759 } 760 761 bpfMap, found := bpfElf.Maps[mapName] 762 if !found { 763 return fmt.Errorf("program references undefined map named '%s'", mapName) 764 } 765 766 // Add map to list of maps used by program if not already in list 767 _, found = program.Maps[mapName] 768 if !found { 769 program.Maps[mapName] = bpfMap 770 } 771 772 relLocations := program.MapFDLocations[mapName] 773 if relLocations == nil { 774 relLocations = []uint64{} 775 } 776 777 relLocations = append(relLocations, uint64(progOff)) 778 779 program.MapFDLocations[mapName] = relLocations 780 } 781 } 782 783 // If this program has the .text section appended, we need to resolve any map relocations from that section 784 if txtInsOff != -1 { 785 txtRelocTable, found := bpfElf.relTables[".rel.text"] 786 if !found { 787 continue 788 } 789 790 for _, relocEntry := range txtRelocTable { 791 section := bpfElf.elfFile.Sections[relocEntry.Symbol.Section] 792 793 if section.Name == ".text" { 794 absOff, err := relocEntry.AbsoluteOffset() 795 if err != nil { 796 return fmt.Errorf("unable to calculate absolute offset for relocation entry: %w", err) 797 } 798 799 // Update the imm of the call instruction which points to a relocated function 800 // in the .text section to reflect the current relative offset 801 callInst := &program.Instructions[txtInsOff+int(absOff)/ebpf.BPFInstSize] 802 callInst.Imm = (int32(txtInsOff) + callInst.Imm) - (int32(absOff) / int32(ebpf.BPFInstSize)) 803 804 continue 805 } 806 807 if section.Name == "maps" { 808 mapName := relocEntry.Symbol.Name 809 if bpfElf.settings.TruncateNames && len(mapName) > bpftypes.BPF_OBJ_NAME_LEN-1 { 810 mapName = mapName[:bpftypes.BPF_OBJ_NAME_LEN-1] 811 } 812 813 bpfMap, found := bpfElf.Maps[mapName] 814 if !found { 815 return fmt.Errorf("program .text references undefined map named '%s'", mapName) 816 } 817 818 // Add map to list of maps used by program if not already in list 819 _, found = program.Maps[mapName] 820 if !found { 821 program.Maps[mapName] = bpfMap 822 } 823 824 relLocations := program.MapFDLocations[mapName] 825 if relLocations == nil { 826 relLocations = []uint64{} 827 } 828 829 absOff, err := relocEntry.AbsoluteOffset() 830 if err != nil { 831 return fmt.Errorf("unable to calculate absolute offset for relocation entry: %w", err) 832 } 833 834 // Since the .text section is appended to the main program and the relocation offset is relative 835 // to the start of the .text section we need to add to offset of the .text instructions to the 836 // absolute address. 837 absOff += uint64(txtInsOff * ebpf.BPFInstSize) 838 839 relLocations = append(relLocations, absOff) 840 841 program.MapFDLocations[mapName] = relLocations 842 } 843 } 844 } 845 } 846 847 return nil 848 } 849 850 // specializePrograms takes the abstract programs from the ElfPrograms map and turns them into specialized program 851 // types. 852 func (bpfElf *bpfELF) specializePrograms() error { 853 for name, prog := range bpfElf.ElfPrograms { 854 // Assign license and BTF to abstract program before specializing them. 855 prog.License = bpfElf.license 856 857 specificProgInt := BPFProgramFromAbstract(prog.AbstractBPFProgram) 858 859 sectionParts := strings.Split(prog.section, "/") 860 861 // For some program types the section name can be used to pass additional information about 862 // how the program should be attached 863 switch specificProg := specificProgInt.(type) { 864 case *ProgramTracepoint: 865 if len(sectionParts) >= 2 { 866 specificProg.DefaultCategory = sectionParts[1] 867 } 868 if len(sectionParts) >= 3 { 869 specificProg.DefaultName = sectionParts[2] 870 } 871 872 case *ProgramKProbe: 873 uprobe := false 874 switch sectionParts[0] { 875 case "kprobe": 876 specificProg.DefaultType = perf.TypeKProbe 877 878 case "kretprobe": 879 specificProg.DefaultType = perf.TypeKRetprobe 880 881 case "uprobe": 882 uprobe = true 883 specificProg.DefaultType = perf.TypeUProbe 884 885 case "uretprobe": 886 uprobe = true 887 specificProg.DefaultType = perf.TypeURetProbe 888 } 889 890 if uprobe { 891 var path, offsetStr string 892 if len(sectionParts) == 2 { 893 path = "/" + sectionParts[1] 894 } 895 896 if len(sectionParts) == 3 { 897 path = "/" + sectionParts[1] 898 offsetStr = sectionParts[2] 899 } 900 901 if len(sectionParts) > 3 { 902 path = "/" + strings.Join(sectionParts[1:len(sectionParts)-1], "/") 903 offsetStr = sectionParts[len(sectionParts)-1] 904 } 905 906 specificProg.DefaultPath = path 907 if off, err := strconv.ParseInt(offsetStr, 0, 64); err == nil { 908 specificProg.DefaultOffset = int(off) 909 } 910 911 } else { 912 specificProg.DefaultEvent = name 913 if len(sectionParts) == 2 { 914 specificProg.DefaultSymbol = sectionParts[1] 915 } 916 if len(sectionParts) >= 3 { 917 specificProg.DefaultModule = sectionParts[1] 918 specificProg.DefaultSymbol = sectionParts[2] 919 } 920 } 921 } 922 923 bpfElf.Programs[name] = specificProgInt 924 } 925 926 return nil 927 } 928 929 // toBPFELF converts the internal bpfELF to the external BPFELF version which doesn't contain the intermediate 930 // variables produced during parsing. 931 func (bpfElf *bpfELF) toBPFELF() BPFELF { 932 retBpfELF := BPFELF{ 933 ByteOrder: bpfElf.ByteOrder, 934 Maps: bpfElf.Maps, 935 Programs: bpfElf.Programs, 936 BTF: bpfElf.BTF, 937 } 938 939 return retBpfELF 940 } 941 942 // This map translates ELF section names to program types. 943 // https://github.com/libbpf/libbpf/blob/eaea2bce024fa6ae0db54af1e78b4d477d422791/src/libbpf.c#L8270 944 var sectionNameToProgType = map[string]bpftypes.BPFProgType{ 945 "sock_filter": bpftypes.BPF_PROG_TYPE_SOCKET_FILTER, 946 "socket": bpftypes.BPF_PROG_TYPE_SOCKET_FILTER, 947 "kprobe": bpftypes.BPF_PROG_TYPE_KPROBE, 948 "kretprobe": bpftypes.BPF_PROG_TYPE_KPROBE, 949 "uprobe": bpftypes.BPF_PROG_TYPE_KPROBE, 950 "uretprobe": bpftypes.BPF_PROG_TYPE_KPROBE, 951 "tc_cls": bpftypes.BPF_PROG_TYPE_SCHED_CLS, 952 "tc_act": bpftypes.BPF_PROG_TYPE_SCHED_ACT, 953 "tracepoint": bpftypes.BPF_PROG_TYPE_TRACEPOINT, 954 "xdp": bpftypes.BPF_PROG_TYPE_XDP, 955 "perf_event": bpftypes.BPF_PROG_TYPE_PERF_EVENT, 956 "cgroup_skb": bpftypes.BPF_PROG_TYPE_CGROUP_SKB, 957 "cgroup_sock": bpftypes.BPF_PROG_TYPE_CGROUP_SOCK, 958 "lwt_in": bpftypes.BPF_PROG_TYPE_LWT_IN, 959 "lwt_out": bpftypes.BPF_PROG_TYPE_LWT_OUT, 960 "lwt_xmit": bpftypes.BPF_PROG_TYPE_LWT_XMIT, 961 "sock_opts": bpftypes.BPF_PROG_TYPE_SOCK_OPS, 962 "sk_skb": bpftypes.BPF_PROG_TYPE_SK_SKB, 963 "cgroup_device": bpftypes.BPF_PROG_TYPE_CGROUP_DEVICE, 964 "raw_tracepoint": bpftypes.BPF_PROG_TYPE_RAW_TRACEPOINT, 965 "cgroup_sock_addr": bpftypes.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, 966 "lwt_seg6local": bpftypes.BPF_PROG_TYPE_LWT_SEG6LOCAL, 967 "lirc_mode2": bpftypes.BPF_PROG_TYPE_LIRC_MODE2, 968 "sk_reuseport": bpftypes.BPF_PROG_TYPE_SK_REUSEPORT, 969 "flow_dissector": bpftypes.BPF_PROG_TYPE_FLOW_DISSECTOR, 970 "cgroup_sysctl": bpftypes.BPF_PROG_TYPE_CGROUP_SYSCTL, 971 "raw_tracepoint_writable": bpftypes.BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, 972 "cgroup_sockopt": bpftypes.BPF_PROG_TYPE_CGROUP_SOCKOPT, 973 "tracing": bpftypes.BPF_PROG_TYPE_TRACING, 974 "struct_ops": bpftypes.BPF_PROG_TYPE_STRUCT_OPS, 975 "ext": bpftypes.BPF_PROG_TYPE_EXT, 976 "lsm": bpftypes.BPF_PROG_TYPE_LSM, 977 "sk_lookup": bpftypes.BPF_PROG_TYPE_SK_LOOKUP, 978 "syscall": bpftypes.BPF_PROG_TYPE_SYSCALL, 979 } 980 981 type elfRelocTable []elfRelocEntry 982 983 // elf_r_bpf The BPF ELF reloc types for BPF. 984 // https://github.com/llvm/llvm-project/blob/74d9a76ad3f55c16982ceaa8b6b4a6b7744109b1/llvm/include/llvm/BinaryFormat/ELFRelocs/BPF.def 985 //nolint:lll 986 type elf_r_bpf int 987 988 const ( 989 // r_bpf_none is an invalid relocation type 990 //nolint:deadcode,varcheck // want to keep this here for completeness 991 r_bpf_none elf_r_bpf = 0 992 // r_bpf_64_64 indicates that 32 bits should be relocated 993 r_bpf_64_64 elf_r_bpf = 1 994 // r_bpf_64_32 insicates that 64 bits should be relocated 995 r_bpf_64_32 elf_r_bpf = 10 996 ) 997 998 type elfRelocEntry struct { 999 elf.Rel64 1000 1001 Symbol *elf.Symbol 1002 Type elf_r_bpf 1003 } 1004 1005 func (e *elfRelocEntry) AbsoluteOffset() (uint64, error) { 1006 switch e.Type { 1007 case r_bpf_64_64, r_bpf_none: 1008 // Just the absolute offset from the beginning of the program section 1009 return e.Off, nil 1010 case r_bpf_64_32: 1011 // Just the absolute offset from the beginning of the program section truncated to 32 bits 1012 const _32bitMask = 0x00000000FFFFFFFF 1013 return e.Off & _32bitMask, nil 1014 } 1015 1016 return 0, fmt.Errorf("reloc type not implemented: '%d'", e.Type) 1017 }