LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec.
(rd io.ReaderAt)
| 76 | |
| 77 | // LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec. |
| 78 | func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { |
| 79 | f, err := internal.NewSafeELFFile(rd) |
| 80 | if err != nil { |
| 81 | return nil, err |
| 82 | } |
| 83 | |
| 84 | // Checks if the ELF file is for BPF data. |
| 85 | // Old LLVM versions set e_machine to EM_NONE. |
| 86 | if f.Machine != elf.EM_NONE && f.Machine != elf.EM_BPF { |
| 87 | return nil, fmt.Errorf("unexpected machine type for BPF ELF: %s", f.Machine) |
| 88 | } |
| 89 | |
| 90 | var ( |
| 91 | licenseSection *elf.Section |
| 92 | versionSection *elf.Section |
| 93 | sections = make(map[elf.SectionIndex]*elfSection) |
| 94 | relSections = make(map[elf.SectionIndex]*elf.Section) |
| 95 | ) |
| 96 | |
| 97 | // This is the target of relocations generated by inline assembly. |
| 98 | sections[elf.SHN_UNDEF] = newElfSection(new(elf.Section), undefSection) |
| 99 | |
| 100 | // Collect all the sections we're interested in. This includes relocations |
| 101 | // which we parse later. |
| 102 | // |
| 103 | // Keep the documentation at docs/ebpf/loading/elf-sections.md up-to-date. |
| 104 | for i, sec := range f.Sections { |
| 105 | idx := elf.SectionIndex(i) |
| 106 | |
| 107 | switch { |
| 108 | case strings.HasPrefix(sec.Name, "license"): |
| 109 | licenseSection = sec |
| 110 | case strings.HasPrefix(sec.Name, "version"): |
| 111 | versionSection = sec |
| 112 | case strings.HasPrefix(sec.Name, "maps"): |
| 113 | sections[idx] = newElfSection(sec, mapSection) |
| 114 | case sec.Name == ".maps": |
| 115 | sections[idx] = newElfSection(sec, btfMapSection) |
| 116 | case isDataSection(sec.Name): |
| 117 | sections[idx] = newElfSection(sec, dataSection) |
| 118 | case sec.Type == elf.SHT_REL: |
| 119 | // Store relocations under the section index of the target |
| 120 | relSections[elf.SectionIndex(sec.Info)] = sec |
| 121 | case sec.Type == elf.SHT_PROGBITS && sec.Size > 0: |
| 122 | if (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0 { |
| 123 | sections[idx] = newElfSection(sec, programSection) |
| 124 | } else if sec.Name == structOpsLinkSec { |
| 125 | // classification based on sec names so that struct_ops-specific |
| 126 | // sections (.struct_ops.link) is correctly recognized |
| 127 | // as non-executable PROGBITS, allowing value placement and link metadata to be loaded. |
| 128 | sections[idx] = newElfSection(sec, structOpsSection) |
| 129 | } else if sec.Name == structOpsSec { |
| 130 | return nil, fmt.Errorf("section %q: got '.struct_ops' section: %w", sec.Name, ErrNotSupported) |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | license, err := loadLicense(licenseSection) |
searching dependent graphs…