Skip to content

Commit

Permalink
implementation with btf.Dataspec and Map.mmap and LoadAt StoreAt
Browse files Browse the repository at this point in the history
Signed-off-by: Simone Magnani <[email protected]>
  • Loading branch information
smagnani96 committed Aug 14, 2024
1 parent f513b8f commit 71e3029
Show file tree
Hide file tree
Showing 18 changed files with 793 additions and 413 deletions.
110 changes: 110 additions & 0 deletions btf/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,37 @@ import (
"errors"
"fmt"
"strings"
"text/template"
)

var errNestedTooDeep = errors.New("nested too deep")

// Template for the load/store helper for variables.
const loadStoreFuncHelper = `
// {{.MethodName}}{{.VarIntName}} interacts with the provided map to {{.Action}} the value of the {{.VarName}} variable.
// The {{.DatasecIntName}} map for which the {{.TypeName}} structure has been generated must be provided,
// otherwise an error will be returned.
func (b *{{.TypeName}}) {{.MethodName}}{{.VarIntName}}(m *ebpf.Map) error {
if m.Name() != "{{.DatasecName}}" {
return fmt.Errorf("wrong map provided to {{.MethodName}}{{.VarIntName}}: expected {{.DatasecName}}, got %s", m.Name())
}
return m.{{.MethodName}}At({{.Offset}}, uint32(0), &b.{{.VarIntName}})
}
`

// LoadStoreHelperData hold the needed data to fill the loadStoreFuncHelper template,
// used to generate helper functions for interacting with data sections of the program.
type LoadStoreHelperData struct {
MethodName string
VarName string
VarIntName string
Action string
DatasecIntName string
DatasecName string
TypeName string
Offset uint32
}

// GoFormatter converts a Type to Go syntax.
//
// A zero GoFormatter is valid to use.
Expand Down Expand Up @@ -36,6 +63,15 @@ func (gf *GoFormatter) TypeDeclaration(name string, typ Type) (string, error) {
return gf.w.String(), nil
}

// TypeHelpers generates Go helpers for a BTF type.
func (gf *GoFormatter) TypeHelpers(name string, typ Type) (string, error) {
gf.w.Reset()
if err := gf.writeTypeHelpers(name, typ); err != nil {
return "", err
}
return gf.w.String(), nil
}

func (gf *GoFormatter) identifier(s string) string {
if gf.Identifier != nil {
return gf.Identifier(s)
Expand Down Expand Up @@ -90,6 +126,33 @@ func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error {
return nil
}

// writeTypeHelpers outputs helpers for the given type.
//
// It encodes https://go.dev/ref/spec#Method_declarations:
//
// func (r *Receiver) FuncName(params ...) (return values...) {...}
func (gf *GoFormatter) writeTypeHelpers(name string, typ Type) error {
if name == "" {
return fmt.Errorf("need a name for type %s helpers", typ)
}

typ = skipQualifiers(typ)

var err error
switch v := skipQualifiers(typ).(type) {
case *Datasec:
err = gf.writeDatasecFunc(gf.Names[typ], v)
default:
return fmt.Errorf("helpers for type %T: %w", v, ErrNotSupported)
}

if err != nil {
return fmt.Errorf("%s: %w", typ, err)
}

return nil
}

// writeType outputs the name of a named type or a literal describing the type.
//
// It encodes https://golang.org/ref/spec#Types.
Expand Down Expand Up @@ -330,6 +393,53 @@ func (gf *GoFormatter) writeDatasecLit(ds *Datasec, depth int) error {
return nil
}

func (gf *GoFormatter) writeDatasecFunc(typeName string, ds *Datasec) error {

tmpl, err := template.New("helper").Parse(loadStoreFuncHelper)
if err != nil {
return fmt.Errorf("failed to parse template: %w", err)
}

prevOffset := uint32(0)
for i, vsi := range ds.Vars {
v, ok := vsi.Type.(*Var)

if !ok {
return fmt.Errorf("can't format %s as part of data section", vsi.Type)
}

if v.Linkage != GlobalVar {
// Ignore static, extern, etc. for now.
continue
}

if v.Name == "" {
return fmt.Errorf("variable %d: empty name", i)
}

for _, hName := range []string{"Load", "Store"} {
data := LoadStoreHelperData{
MethodName: hName,
VarName: v.Name,
VarIntName: gf.identifier(v.Name),
Action: strings.ToLower(hName),
DatasecIntName: gf.identifier(ds.Name),
DatasecName: ds.Name,
TypeName: typeName,
Offset: prevOffset,
}

if err := tmpl.Execute(&gf.w, data); err != nil {
return fmt.Errorf("failed to execute template for %s%s: %w", hName, data.VarIntName, err)
}
}

prevOffset = vsi.Offset + vsi.Size
}

return nil
}

func (gf *GoFormatter) writePadding(bytes uint32) {
if bytes > 0 {
fmt.Fprintf(&gf.w, "_ [%d]byte; ", bytes)
Expand Down
21 changes: 0 additions & 21 deletions cmd/bpf2go/gen/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ func (n templateName) MapSpecs() string {
return string(n) + "MapSpecs"
}

func (n templateName) VariableSpecs() string {
return string(n) + "VariableSpecs"
}

func (n templateName) Load() string {
return n.maybeExport("load" + toUpperFirst(string(n)))
}
Expand Down Expand Up @@ -86,8 +82,6 @@ type GenerateArgs struct {
Constraints constraint.Expr
// Maps to be emitted.
Maps []string
// Variables to be emitted.
Variables []string
// Programs to be emitted.
Programs []string
// Types to be emitted.
Expand All @@ -109,24 +103,11 @@ func Generate(args GenerateArgs) error {
return fmt.Errorf("file %q contains an invalid character", args.ObjectFile)
}

for _, typ := range args.Types {
if _, ok := btf.As[*btf.Datasec](typ); ok {
// Avoid emitting .rodata, .bss, etc. for now. We might want to
// name these types differently, etc.
return fmt.Errorf("can't output btf.Datasec: %s", typ)
}
}

maps := make(map[string]string)
for _, name := range args.Maps {
maps[name] = internal.Identifier(name)
}

vars := make(map[string]string)
for _, name := range args.Variables {
vars[name] = internal.Identifier(name)
}

programs := make(map[string]string)
for _, name := range args.Programs {
programs[name] = internal.Identifier(name)
Expand Down Expand Up @@ -157,7 +138,6 @@ func Generate(args GenerateArgs) error {
Constraints constraint.Expr
Name templateName
Maps map[string]string
Variables map[string]string
Programs map[string]string
Types []btf.Type
TypeNames map[btf.Type]string
Expand All @@ -169,7 +149,6 @@ func Generate(args GenerateArgs) error {
args.Constraints,
templateName(args.Stem),
maps,
vars,
programs,
types,
typeNames,
Expand Down
11 changes: 1 addition & 10 deletions cmd/bpf2go/gen/output.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
{{- if .Types }}
{{- range $type := .Types }}
{{ $.TypeDeclaration (index $.TypeNames $type) $type }}
{{ $.TypeHelpers (index $.TypeNames $type) $type }}

{{ end }}
{{- end }}
Expand Down Expand Up @@ -54,7 +55,6 @@ func {{ .Name.LoadObjects }}(obj interface{}, opts *ebpf.CollectionOptions) (err
type {{ .Name.Specs }} struct {
{{ .Name.ProgramSpecs }}
{{ .Name.MapSpecs }}
{{ .Name.VariableSpecs }}
}

// {{ .Name.Specs }} contains programs before they are loaded into the kernel.
Expand Down Expand Up @@ -107,15 +107,6 @@ func (m *{{ .Name.Maps }}) Close() error {
)
}

// {{ .Name.VariableSpecs }} contains variables before they are loaded into the kernel.
//
// It can be passed ebpf.CollectionSpec.Assign.
type {{ .Name.VariableSpecs }} struct {
{{- range $name, $id := .Variables }}
{{ $id }} *ebpf.VariableSpec `ebpf:"{{ $name }}"`
{{- end }}
}

// {{ .Name.Programs }} contains all programs after they have been loaded into the kernel.
//
// It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign.
Expand Down
4 changes: 0 additions & 4 deletions cmd/bpf2go/gen/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ func CollectGlobalTypes(spec *ebpf.CollectionSpec) []btf.Type {
var types []btf.Type
for _, typ := range collectMapTypes(spec.Maps) {
switch btf.UnderlyingType(typ).(type) {
case *btf.Datasec:
// Avoid emitting .rodata, .bss, etc. for now. We might want to
// name these types differently, etc.
continue

case *btf.Int:
// Don't emit primitive types by default.
Expand Down
6 changes: 0 additions & 6 deletions cmd/bpf2go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,11 +367,6 @@ func (b2g *bpf2go) convert(tgt gen.Target, goarches gen.GoArches) (err error) {
maps = append(maps, name)
}

var vars []string
for name := range spec.Variables {
vars = append(vars, name)
}

var programs []string
for name := range spec.Programs {
programs = append(programs, name)
Expand Down Expand Up @@ -399,7 +394,6 @@ func (b2g *bpf2go) convert(tgt gen.Target, goarches gen.GoArches) (err error) {
Stem: b2g.identStem,
Constraints: constraints,
Maps: maps,
Variables: vars,
Programs: programs,
Types: types,
ObjectFile: filepath.Base(objFileName),
Expand Down
16 changes: 2 additions & 14 deletions collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ type CollectionOptions struct {

// CollectionSpec describes a collection.
type CollectionSpec struct {
Maps map[string]*MapSpec
Variables map[string]*VariableSpec
Programs map[string]*ProgramSpec
Maps map[string]*MapSpec
Programs map[string]*ProgramSpec

// Types holds type information about Maps and Programs.
// Modifications to Types are currently undefined behaviour.
Expand All @@ -56,7 +55,6 @@ func (cs *CollectionSpec) Copy() *CollectionSpec {

cpy := CollectionSpec{
Maps: make(map[string]*MapSpec, len(cs.Maps)),
Variables: make(map[string]*VariableSpec, len(cs.Variables)),
Programs: make(map[string]*ProgramSpec, len(cs.Programs)),
ByteOrder: cs.ByteOrder,
Types: cs.Types.Copy(),
Expand All @@ -66,10 +64,6 @@ func (cs *CollectionSpec) Copy() *CollectionSpec {
cpy.Maps[name] = spec.Copy()
}

for varName, specs := range cs.Variables {
cpy.Variables[varName] = specs
}

for name, spec := range cs.Programs {
cpy.Programs[name] = spec.Copy()
}
Expand Down Expand Up @@ -486,12 +480,6 @@ func (cl *collectionLoader) loadMap(mapName string) (*Map, error) {
}
}

for _, spec := range cl.coll.Variables {
if spec.MapName == mapName {
m.assignVariables(spec)
}
}

cl.maps[mapName] = m
return m, nil
}
Expand Down
8 changes: 0 additions & 8 deletions collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,6 @@ func TestCollectionSpecCopy(t *testing.T) {
MaxEntries: 1,
},
},
map[string]*VariableSpec{
"attempt": {
Name: "attempt",
MapName: ".rodata",
Offset: 0,
Size: 4,
},
},
map[string]*ProgramSpec{
"test": {
Type: SocketFilter,
Expand Down
15 changes: 2 additions & 13 deletions elf_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ type elfCode struct {
btf *btf.Spec
extInfo *btf.ExtInfos
maps map[string]*MapSpec
vars map[string]*VariableSpec
kfuncs map[string]*btf.Func
kconfig *MapSpec
}
Expand Down Expand Up @@ -135,7 +134,6 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
btf: btfSpec,
extInfo: btfExtInfo,
maps: make(map[string]*MapSpec),
vars: make(map[string]*VariableSpec),
kfuncs: make(map[string]*btf.Func),
}

Expand Down Expand Up @@ -176,7 +174,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
return nil, fmt.Errorf("load programs: %w", err)
}

return &CollectionSpec{ec.maps, ec.vars, progs, btfSpec, ec.ByteOrder}, nil
return &CollectionSpec{ec.maps, progs, btfSpec, ec.ByteOrder}, nil
}

func loadLicense(sec *elf.Section) (string, error) {
Expand Down Expand Up @@ -1100,15 +1098,6 @@ func (ec *elfCode) loadDataSections() error {
continue
}

for off, sym := range sec.symbols {
ec.vars[sym.Name] = &VariableSpec{
Name: sym.Name,
MapName: sec.Name,
Offset: off,
Size: sym.Size,
}
}

var flags uint32
if haveFeatErr := haveMmapableMaps(); haveFeatErr == nil {
flags = uint32(sys.BPF_F_MMAPABLE)
Expand Down Expand Up @@ -1156,7 +1145,7 @@ func (ec *elfCode) loadDataSections() error {
}

if strings.HasPrefix(sec.Name, ".rodata") {
mapSpec.Flags = unix.BPF_F_RDONLY_PROG
mapSpec.Flags |= unix.BPF_F_RDONLY_PROG
mapSpec.Freeze = true
}

Expand Down
Loading

0 comments on commit 71e3029

Please sign in to comment.