123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- package ebpf
- import (
- "bytes"
- "debug/elf"
- "encoding/binary"
- "fmt"
- "io"
- "os"
- "strings"
- "github.com/cilium/ebpf/asm"
- "github.com/pkg/errors"
- )
- type elfCode struct {
- *elf.File
- symbols []elf.Symbol
- symbolsPerSection map[elf.SectionIndex]map[uint64]string
- }
- // LoadCollectionSpec parses an ELF file into a CollectionSpec.
- func LoadCollectionSpec(file string) (*CollectionSpec, error) {
- f, err := os.Open(file)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- spec, err := LoadCollectionSpecFromReader(f)
- return spec, errors.Wrapf(err, "file %s", file)
- }
- // LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec.
- func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) {
- f, err := elf.NewFile(code)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- symbols, err := f.Symbols()
- if err != nil {
- return nil, errors.Wrap(err, "load symbols")
- }
- ec := &elfCode{f, symbols, symbolsPerSection(symbols)}
- var licenseSection, versionSection *elf.Section
- progSections := make(map[elf.SectionIndex]*elf.Section)
- relSections := make(map[elf.SectionIndex]*elf.Section)
- mapSections := make(map[elf.SectionIndex]*elf.Section)
- for i, sec := range ec.Sections {
- switch {
- case strings.HasPrefix(sec.Name, "license"):
- licenseSection = sec
- case strings.HasPrefix(sec.Name, "version"):
- versionSection = sec
- case strings.HasPrefix(sec.Name, "maps"):
- mapSections[elf.SectionIndex(i)] = sec
- case sec.Type == elf.SHT_REL:
- if int(sec.Info) >= len(ec.Sections) {
- return nil, errors.Errorf("found relocation section %v for missing section %v", i, sec.Info)
- }
- // Store relocations under the section index of the target
- idx := elf.SectionIndex(sec.Info)
- if relSections[idx] != nil {
- return nil, errors.Errorf("section %d has multiple relocation sections", idx)
- }
- relSections[idx] = sec
- case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0:
- progSections[elf.SectionIndex(i)] = sec
- }
- }
- license, err := loadLicense(licenseSection)
- if err != nil {
- return nil, errors.Wrap(err, "load license")
- }
- version, err := loadVersion(versionSection, ec.ByteOrder)
- if err != nil {
- return nil, errors.Wrap(err, "load version")
- }
- maps, err := ec.loadMaps(mapSections)
- if err != nil {
- return nil, errors.Wrap(err, "load maps")
- }
- progs, libs, err := ec.loadPrograms(progSections, relSections, license, version)
- if err != nil {
- return nil, errors.Wrap(err, "load programs")
- }
- if len(libs) > 0 {
- for name, prog := range progs {
- prog.Instructions, err = link(prog.Instructions, libs...)
- if err != nil {
- return nil, errors.Wrapf(err, "program %s", name)
- }
- }
- }
- return &CollectionSpec{maps, progs}, nil
- }
- func loadLicense(sec *elf.Section) (string, error) {
- if sec == nil {
- return "", errors.Errorf("missing license section")
- }
- data, err := sec.Data()
- if err != nil {
- return "", errors.Wrapf(err, "section %s", sec.Name)
- }
- return string(bytes.TrimRight(data, "\000")), nil
- }
- func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) {
- if sec == nil {
- return 0, nil
- }
- var version uint32
- err := binary.Read(sec.Open(), bo, &version)
- return version, errors.Wrapf(err, "section %s", sec.Name)
- }
- func (ec *elfCode) loadPrograms(progSections, relSections map[elf.SectionIndex]*elf.Section, license string, version uint32) (map[string]*ProgramSpec, []asm.Instructions, error) {
- var (
- progs = make(map[string]*ProgramSpec)
- libs []asm.Instructions
- )
- for idx, prog := range progSections {
- syms := ec.symbolsPerSection[idx]
- if len(syms) == 0 {
- return nil, nil, errors.Errorf("section %v: missing symbols", prog.Name)
- }
- funcSym := syms[0]
- if funcSym == "" {
- return nil, nil, errors.Errorf("section %v: no label at start", prog.Name)
- }
- rels, err := ec.loadRelocations(relSections[idx])
- if err != nil {
- return nil, nil, errors.Wrapf(err, "program %s: can't load relocations", funcSym)
- }
- insns, err := ec.loadInstructions(prog, syms, rels)
- if err != nil {
- return nil, nil, errors.Wrapf(err, "program %s: can't unmarshal instructions", funcSym)
- }
- if progType, attachType := getProgType(prog.Name); progType == UnspecifiedProgram {
- // There is no single name we can use for "library" sections,
- // since they may contain multiple functions. We'll decode the
- // labels they contain later on, and then link sections that way.
- libs = append(libs, insns)
- } else {
- progs[funcSym] = &ProgramSpec{
- Name: funcSym,
- Type: progType,
- AttachType: attachType,
- License: license,
- KernelVersion: version,
- Instructions: insns,
- }
- }
- }
- return progs, libs, nil
- }
- func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]string) (asm.Instructions, error) {
- var (
- r = section.Open()
- insns asm.Instructions
- ins asm.Instruction
- offset uint64
- )
- for {
- n, err := ins.Unmarshal(r, ec.ByteOrder)
- if err == io.EOF {
- return insns, nil
- }
- if err != nil {
- return nil, errors.Wrapf(err, "offset %d", offset)
- }
- ins.Symbol = symbols[offset]
- ins.Reference = relocations[offset]
- insns = append(insns, ins)
- offset += n
- }
- }
- func (ec *elfCode) loadMaps(mapSections map[elf.SectionIndex]*elf.Section) (map[string]*MapSpec, error) {
- var (
- maps = make(map[string]*MapSpec)
- b = make([]byte, 1)
- )
- for idx, sec := range mapSections {
- syms := ec.symbolsPerSection[idx]
- if len(syms) == 0 {
- return nil, errors.Errorf("section %v: no symbols", sec.Name)
- }
- if sec.Size%uint64(len(syms)) != 0 {
- return nil, errors.Errorf("section %v: map descriptors are not of equal size", sec.Name)
- }
- var (
- r = sec.Open()
- size = sec.Size / uint64(len(syms))
- )
- for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size {
- mapSym := syms[offset]
- if mapSym == "" {
- fmt.Println(syms)
- return nil, errors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
- }
- if maps[mapSym] != nil {
- return nil, errors.Errorf("section %v: map %v already exists", sec.Name, mapSym)
- }
- lr := io.LimitReader(r, int64(size))
- var spec MapSpec
- switch {
- case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
- return nil, errors.Errorf("map %v: missing type", mapSym)
- case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil:
- return nil, errors.Errorf("map %v: missing key size", mapSym)
- case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil:
- return nil, errors.Errorf("map %v: missing value size", mapSym)
- case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil:
- return nil, errors.Errorf("map %v: missing max entries", mapSym)
- case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil:
- return nil, errors.Errorf("map %v: missing flags", mapSym)
- }
- for {
- _, err := lr.Read(b)
- if err == io.EOF {
- break
- }
- if err != nil {
- return nil, err
- }
- if b[0] != 0 {
- return nil, errors.Errorf("map %v: unknown and non-zero fields in definition", mapSym)
- }
- }
- maps[mapSym] = &spec
- }
- }
- return maps, nil
- }
- func getProgType(v string) (ProgramType, AttachType) {
- types := map[string]ProgramType{
- // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c#n3568
- "socket": SocketFilter,
- "seccomp": SocketFilter,
- "kprobe/": Kprobe,
- "kretprobe/": Kprobe,
- "tracepoint/": TracePoint,
- "xdp": XDP,
- "perf_event": PerfEvent,
- "sockops": SockOps,
- "sk_skb": SkSKB,
- "sk_msg": SkMsg,
- "lirc_mode2": LircMode2,
- "flow_dissector": FlowDissector,
- "cgroup_skb/": CGroupSKB,
- "cgroup/dev": CGroupDevice,
- "cgroup/skb": CGroupSKB,
- "cgroup/sock": CGroupSock,
- "cgroup/post_bind": CGroupSock,
- "cgroup/bind": CGroupSockAddr,
- "cgroup/connect": CGroupSockAddr,
- "cgroup/sendmsg": CGroupSockAddr,
- "cgroup/recvmsg": CGroupSockAddr,
- "cgroup/sysctl": CGroupSysctl,
- "cgroup/getsockopt": CGroupSockopt,
- "cgroup/setsockopt": CGroupSockopt,
- "classifier": SchedCLS,
- "action": SchedACT,
- }
- attachTypes := map[string]AttachType{
- "cgroup_skb/ingress": AttachCGroupInetIngress,
- "cgroup_skb/egress": AttachCGroupInetEgress,
- "cgroup/sock": AttachCGroupInetSockCreate,
- "cgroup/post_bind4": AttachCGroupInet4PostBind,
- "cgroup/post_bind6": AttachCGroupInet6PostBind,
- "cgroup/dev": AttachCGroupDevice,
- "sockops": AttachCGroupSockOps,
- "sk_skb/stream_parser": AttachSkSKBStreamParser,
- "sk_skb/stream_verdict": AttachSkSKBStreamVerdict,
- "sk_msg": AttachSkSKBStreamVerdict,
- "lirc_mode2": AttachLircMode2,
- "flow_dissector": AttachFlowDissector,
- "cgroup/bind4": AttachCGroupInet4Bind,
- "cgroup/bind6": AttachCGroupInet6Bind,
- "cgroup/connect4": AttachCGroupInet4Connect,
- "cgroup/connect6": AttachCGroupInet6Connect,
- "cgroup/sendmsg4": AttachCGroupUDP4Sendmsg,
- "cgroup/sendmsg6": AttachCGroupUDP6Sendmsg,
- "cgroup/recvmsg4": AttachCGroupUDP4Recvmsg,
- "cgroup/recvmsg6": AttachCGroupUDP6Recvmsg,
- "cgroup/sysctl": AttachCGroupSysctl,
- "cgroup/getsockopt": AttachCGroupGetsockopt,
- "cgroup/setsockopt": AttachCGroupSetsockopt,
- }
- attachType := AttachNone
- for k, t := range attachTypes {
- if strings.HasPrefix(v, k) {
- attachType = t
- }
- }
- for k, t := range types {
- if strings.HasPrefix(v, k) {
- return t, attachType
- }
- }
- return UnspecifiedProgram, AttachNone
- }
- func (ec *elfCode) loadRelocations(sec *elf.Section) (map[uint64]string, error) {
- rels := make(map[uint64]string)
- if sec == nil {
- return rels, nil
- }
- if sec.Entsize < 16 {
- return nil, errors.New("rels are less than 16 bytes")
- }
- r := sec.Open()
- for off := uint64(0); off < sec.Size; off += sec.Entsize {
- ent := io.LimitReader(r, int64(sec.Entsize))
- var rel elf.Rel64
- if binary.Read(ent, ec.ByteOrder, &rel) != nil {
- return nil, errors.Errorf("can't parse relocation at offset %v", off)
- }
- symNo := int(elf.R_SYM64(rel.Info) - 1)
- if symNo >= len(ec.symbols) {
- return nil, errors.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo)
- }
- rels[rel.Off] = ec.symbols[symNo].Name
- }
- return rels, nil
- }
- func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]string {
- result := make(map[elf.SectionIndex]map[uint64]string)
- for i, sym := range symbols {
- switch elf.ST_TYPE(sym.Info) {
- case elf.STT_NOTYPE:
- // Older versions of LLVM doesn't tag
- // symbols correctly.
- break
- case elf.STT_OBJECT:
- break
- case elf.STT_FUNC:
- break
- default:
- continue
- }
- if sym.Name == "" {
- continue
- }
- idx := sym.Section
- if _, ok := result[idx]; !ok {
- result[idx] = make(map[uint64]string)
- }
- result[idx][sym.Value] = symbols[i].Name
- }
- return result
- }
|