Extended Berkeley Packet Filter (eBPF) assembler and virtual machine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

210 lines
5.2 KiB

package ebpf
import (
"bytes"
"debug/elf"
"encoding/binary"
"errors"
"fmt"
"io"
"log"
)
const (
maxELFSections = 32
)
var (
errELFClass = errors.New("ebpf: invalid ELF class")
errELFByteOrder = errors.New("ebpf: invalid ELF byte order")
errELFVersion = errors.New("ebpf: invalid ELF version")
errELFOSABI = errors.New("ebpf: invalid ELF OS ABI")
errELFRelocatable = errors.New("ebpf: invalid ELF not relocatable")
errTextSection = errors.New("ebpf: text section not found")
)
// LoadELF loads eBPF program(s) from an ELF file.
//
// This function is not finished and work-in-progress. Expect breakage.
func LoadELF(r io.ReaderAt) (map[string]Program, error) {
file, err := elf.NewFile(r)
if err != nil {
return nil, err
}
if file.Class != elf.ELFCLASS64 {
return nil, errELFClass
}
if file.Data != elf.ELFDATA2LSB {
return nil, errELFByteOrder
}
if file.Version != 1 {
return nil, errELFVersion
}
if file.OSABI != 0 {
return nil, errELFOSABI
}
if file.Type != elf.ET_REL {
return nil, errELFRelocatable
}
if file.Machine != elf.EM_NONE && file.Machine != elf.EM_BPF {
return nil, fmt.Errorf("ebpf: invalid ELF machine, expected BPF, got %d", file.Machine)
}
if l := len(file.Sections); l > maxELFSections {
return nil, fmt.Errorf("ebpf: %d ELF sections exceeds maximum of %d", l, maxELFSections)
}
var textSection *elf.Section
for _, section := range file.Sections {
if section.Type == elf.SHT_PROGBITS && section.Flags == (elf.SHF_ALLOC|elf.SHF_EXECINSTR) {
//log.Printf("text section: %#+v", section)
textSection = section
break
}
}
if textSection == nil {
return nil, errTextSection
}
var textData = make([]byte, textSection.Size)
data, err := textSection.Data()
if err != nil {
return nil, err
}
copy(textData, data)
/* Process each relocation rel */
for _, rel := range file.Sections {
if rel.Type != elf.SHT_REL {
continue
}
log.Printf("rel: %#+v", rel)
if rel.SectionHeader.Link >= uint32(len(file.Sections)) {
return nil, errors.New("ebpf: bad symbol table section index")
}
symbols, _, err := getSymbols(file, file.Sections[rel.SectionHeader.Link])
log.Printf("symbols: %#+v", symbols)
rs, err := parseRelocations(file, rel)
if err != nil {
return nil, err
}
log.Printf("relocations: %#+v", rs)
for _, r := range rs {
var (
_ = r.Info & 0xffffffff // relType
symIndex = r.Info >> 32
)
if symIndex >= uint64(len(symbols)) {
return nil, errors.New("ebpf: bad symbol index")
}
log.Println("relocate to", symbols[symIndex])
}
}
programs := make(map[string]Program)
for _, section := range file.Sections {
if section == textSection {
continue
}
if section.Type == elf.SHT_PROGBITS && section.Flags == (elf.SHF_ALLOC|elf.SHF_EXECINSTR) {
var source []byte
if source, err = section.Data(); err != nil {
return nil, err
}
if programs[section.Name], err = Disassemble(source); err != nil {
return nil, fmt.Errorf("ebpf: error disassembling %q section: %w", section.Name, err)
}
}
}
return programs, nil
}
func parseRelocations(file *elf.File, rel *elf.Section) (rs []elf.Rel64, err error) {
var b []byte
if b, err = rel.Data(); err != nil {
return
}
if l := len(b); l%16 != 0 {
return nil, fmt.Errorf("length of relocation section size %d is not a multiple of 16", l)
}
buf := bytes.NewBuffer(b)
for buf.Len() > 0 {
var r elf.Rel64
if err = binary.Read(buf, file.ByteOrder, &r); err != nil {
return
}
rs = append(rs, r)
}
return
}
func getSymbols(file *elf.File, section *elf.Section) ([]elf.Symbol, []byte, error) {
data, err := section.Data()
if err != nil {
return nil, nil, errors.New("cannot load symbol section")
}
symtab := bytes.NewReader(data)
if symtab.Len()%elf.Sym64Size != 0 {
return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")
}
strdata, err := stringTable(file, section.Link)
if err != nil {
return nil, nil, errors.New("cannot load string table section")
}
// The first entry is all zeros.
var skip [elf.Sym64Size]byte
symtab.Read(skip[:])
symbols := make([]elf.Symbol, symtab.Len()/elf.Sym64Size)
i := 0
var sym elf.Sym64
for symtab.Len() > 0 {
binary.Read(symtab, file.ByteOrder, &sym)
str, _ := getString(strdata, int(sym.Name))
symbols[i].Name = str
symbols[i].Info = sym.Info
symbols[i].Other = sym.Other
symbols[i].Section = elf.SectionIndex(sym.Shndx)
symbols[i].Value = sym.Value
symbols[i].Size = sym.Size
i++
}
return symbols, strdata, nil
}
// stringTable reads and returns the string table given by the
// specified link value.
func stringTable(f *elf.File, link uint32) ([]byte, error) {
if link <= 0 || link >= uint32(len(f.Sections)) {
return nil, errors.New("section has invalid string table link")
}
return f.Sections[link].Data()
}
// getString extracts a string from an ELF string table.
func getString(section []byte, start int) (string, bool) {
if start < 0 || start >= len(section) {
return "", false
}
for end := start; end < len(section); end++ {
if section[end] == 0 {
return string(section[start:end]), true
}
}
return "", false
}
func applyRelocations(dst, relocations []byte) error {
return nil
}