Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ TARGETS := \
testdata/constants \
testdata/errors \
testdata/variables \
testdata/arena \
btf/testdata/relocs \
btf/testdata/relocs_read \
btf/testdata/relocs_read_tgt \
Expand Down
58 changes: 37 additions & 21 deletions elf_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -873,10 +873,11 @@ func (ec *elfCode) loadBTFMaps() error {
func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) {
var (
key, value btf.Type
keySize, valueSize uint32
keySize, valueSize uint64
mapType MapType
flags, maxEntries uint32
flags, maxEntries uint64
pinType PinType
mapExtra uint64
innerMapSpec *MapSpec
contents []MapKV
err error
Expand Down Expand Up @@ -920,7 +921,7 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
return nil, fmt.Errorf("can't get size of BTF key: %w", err)
}

keySize = uint32(size)
keySize = uint64(size)

case "value":
if valueSize != 0 {
Expand All @@ -939,7 +940,7 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
return nil, fmt.Errorf("can't get size of BTF value: %w", err)
}

valueSize = uint32(size)
valueSize = uint64(size)

case "key_size":
// Key needs to be nil and keySize needs to be 0 for key_size to be
Expand Down Expand Up @@ -1035,7 +1036,10 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
}

case "map_extra":
return nil, fmt.Errorf("BTF map definition: field %s: %w", member.Name, ErrNotSupported)
mapExtra, err = uintFromBTF(member.Type)
if err != nil {
return nil, fmt.Errorf("resolving map_extra: %w", err)
}

default:
return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name)
Expand All @@ -1057,33 +1061,45 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
return &MapSpec{
Name: sanitizeName(name, -1),
Type: MapType(mapType),
KeySize: keySize,
ValueSize: valueSize,
MaxEntries: maxEntries,
Flags: flags,
KeySize: uint32(keySize),
ValueSize: uint32(valueSize),
MaxEntries: uint32(maxEntries),
Flags: uint32(flags),
Key: key,
Value: value,
Pinning: pinType,
InnerMap: innerMapSpec,
Contents: contents,
Tags: slices.Clone(v.Tags),
MapExtra: mapExtra,
}, nil
}

// uintFromBTF resolves the __uint macro, which is a pointer to a sized
// array, e.g. for int (*foo)[10], this function will return 10.
func uintFromBTF(typ btf.Type) (uint32, error) {
ptr, ok := typ.(*btf.Pointer)
if !ok {
return 0, fmt.Errorf("not a pointer: %v", typ)
}
// uintFromBTF resolves the __uint and __ulong macros.
//
// __uint emits a pointer to a sized array. For int (*foo)[10], this function
// will return 10.
//
// __ulong emits an enum with a single value that can represent a 64-bit
// integer. The first (and only) enum value is returned.
func uintFromBTF(typ btf.Type) (uint64, error) {
switch t := typ.(type) {
case *btf.Pointer:
arr, ok := t.Target.(*btf.Array)
if !ok {
return 0, fmt.Errorf("not a pointer to array: %v", typ)
}
return uint64(arr.Nelems), nil

arr, ok := ptr.Target.(*btf.Array)
if !ok {
return 0, fmt.Errorf("not a pointer to array: %v", typ)
}
case *btf.Enum:
if len(t.Values) == 0 {
return 0, errors.New("enum has no values")
}
return t.Values[0].Value, nil

return arr.Nelems, nil
default:
return 0, fmt.Errorf("not a pointer or enum: %v", typ)
}
}

// resolveBTFArrayMacro resolves the __array macro, which declares an array
Expand Down
26 changes: 24 additions & 2 deletions elf_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,30 @@ func TestIPRoute2Compat(t *testing.T) {
coll.Close()
}

func TestArena(t *testing.T) {
file := testutils.NativeFile(t, "testdata/arena-%s.elf")
coll, err := LoadCollectionSpec(file)
qt.Assert(t, qt.IsNil(err))

want := &CollectionSpec{
Maps: map[string]*MapSpec{
"arena": {
Name: "arena",
Type: Arena,
MaxEntries: 100,
Flags: sys.BPF_F_MMAPABLE,
MapExtra: 1 << 44,
},
},
Programs: map[string]*ProgramSpec{},
Variables: map[string]*VariableSpec{},
}
qt.Assert(t, qt.CmpEquals(coll, want, csCmpOpts))

testutils.SkipOnOldKernel(t, "6.9", "arena maps")
mustNewCollection(t, coll, nil)
}

var (
elfPath = flag.String("elfs", os.Getenv("CI_KERNEL_SELFTESTS"), "`Path` containing libbpf-compatible ELFs (defaults to $CI_KERNEL_SELFTESTS)")
elfPattern = flag.String("elf-pattern", "*.o", "Glob `pattern` for object files that should be tested")
Expand Down Expand Up @@ -997,8 +1021,6 @@ func TestLibBPFCompat(t *testing.T) {
t.Skip("Skipping since the test generates dynamic BTF")
case "test_static_linked":
t.Skip("Skipping since .text contains 'subprog' twice")
case "bloom_filter_map", "bloom_filter_bench":
t.Skip("Skipping due to missing MapExtra field in MapSpec")
case "netif_receive_skb",
"local_kptr_stash",
"local_kptr_stash_fail",
Expand Down
6 changes: 6 additions & 0 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ type MapSpec struct {
// InnerMap is used as a template for ArrayOfMaps and HashOfMaps
InnerMap *MapSpec

// MapExtra is an opaque field whose meaning is map-specific.
//
// Available from 5.16.
MapExtra uint64

// Extra trailing bytes found in the ELF map definition when using structs
// larger than libbpf's bpf_map_def. nil if no trailing bytes were present.
// Must be nil or empty before instantiating the MapSpec into a Map.
Expand Down Expand Up @@ -534,6 +539,7 @@ func (spec *MapSpec) createMap(inner *sys.FD) (_ *Map, err error) {
MaxEntries: spec.MaxEntries,
MapFlags: spec.Flags,
NumaNode: spec.NumaNode,
MapExtra: spec.MapExtra,
}

if inner != nil {
Expand Down
1 change: 1 addition & 0 deletions map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func TestMapSpecCopy(t *testing.T) {
1,
[]MapKV{{1, 2}}, // Can't copy Contents, use value types
nil, // InnerMap
0, // MapExtra
bytes.NewReader(nil),
&btf.Int{},
&btf.Int{},
Expand Down
Binary file added testdata/arena-eb.elf
Binary file not shown.
Binary file added testdata/arena-el.elf
Binary file not shown.
10 changes: 10 additions & 0 deletions testdata/arena.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* This file excercises the ELF loader. It is not a valid BPF program. */

#include "common.h"

struct {
__uint(type, BPF_MAP_TYPE_ARENA);
__uint(map_flags, BPF_F_MMAPABLE);
__uint(max_entries, 100); /* number of pages */
__ulong(map_extra, 0x1ull << 44); /* start of mmap region */
} arena __section(".maps");
6 changes: 6 additions & 0 deletions testdata/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ enum libbpf_tristate {
TRI_MODULE = 2,
};

#define ___bpf_concat(a, b) ____bpf_concat(a, b)
#define ____bpf_concat(a, b) a ## b

#define __section(NAME) __attribute__((section(NAME), used))
#define __uint(name, val) int(*name)[val]
#define __type(name, val) typeof(val) *name
#define __array(name, val) typeof(val) *name[]
#define __ulong(name, val) enum { ___bpf_concat(__unique_value, __COUNTER__) = val } name

#define __kconfig __attribute__((section(".kconfig")))
#define __ksym __attribute__((section(".ksyms")))
Expand All @@ -40,8 +44,10 @@ enum libbpf_tristate {
#define BPF_MAP_TYPE_PERF_EVENT_ARRAY (4)
#define BPF_MAP_TYPE_ARRAY_OF_MAPS (12)
#define BPF_MAP_TYPE_HASH_OF_MAPS (13)
#define BPF_MAP_TYPE_ARENA (33)

#define BPF_F_NO_PREALLOC (1U << 0)
#define BPF_F_MMAPABLE (1U << 10)
#define BPF_F_CURRENT_CPU (0xffffffffULL)

/* From tools/lib/bpf/libbpf.h */
Expand Down