Repository: cilium/ebpf
Pull request #54 (2020-01-29 11:23:17)

map: populate name from ELF symbol name
Use the symbol used to refer to a map as the default name. Since Linux 5.2
it's also valid to include '.' in a name, so make SanitizeName account for
this.


Patch:
diff --git a/elf_reader.go b/elf_reader.go
index c33b744..8b72ce7 100644
--- a/elf_reader.go
+++ b/elf_reader.go
@@ -259,7 +259,9 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio
 
 			lr := io.LimitReader(r, int64(size))
 
-			var spec MapSpec
+			spec := MapSpec{
+				Name: SanitizeName(mapSym, -1),
+			}
 			switch {
 			case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
 				return errors.Errorf("map %v: missing type", mapSym)
diff --git a/map.go b/map.go
index 586cfff..ed3d154 100644
--- a/map.go
+++ b/map.go
@@ -169,13 +169,8 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err
 		attr.btfValueTypeID = btf.MapValue(spec.BTF).ID()
 	}
 
-	name, err := newBPFObjName(spec.Name)
-	if err != nil {
-		return nil, errors.Wrap(err, "map create")
-	}
-
 	if haveObjName() == nil {
-		attr.mapName = name
+		attr.mapName = newBPFObjName(spec.Name)
 	}
 
 	fd, err := bpfMapCreate(&attr)
diff --git a/prog.go b/prog.go
index 08ef4fa..beeb66d 100644
--- a/prog.go
+++ b/prog.go
@@ -197,13 +197,8 @@ func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr
 		license:            internal.NewStringPointer(spec.License),
 	}
 
-	name, err := newBPFObjName(spec.Name)
-	if err != nil {
-		return nil, err
-	}
-
 	if haveObjName() == nil {
-		attr.progName = name
+		attr.progName = newBPFObjName(spec.Name)
 	}
 
 	if handle != nil && spec.BTF != nil {
diff --git a/syscalls.go b/syscalls.go
index 70f8c12..0c8da90 100644
--- a/syscalls.go
+++ b/syscalls.go
@@ -2,7 +2,6 @@ package ebpf
 
 import (
 	"path/filepath"
-	"strings"
 	"unsafe"
 
 	"github.com/cilium/ebpf/internal"
@@ -17,18 +16,15 @@ import (
 type bpfObjName [unix.BPF_OBJ_NAME_LEN]byte
 
 // newBPFObjName truncates the result if it is too long.
-func newBPFObjName(name string) (bpfObjName, error) {
-	idx := strings.IndexFunc(name, invalidBPFObjNameChar)
-	if idx != -1 {
-		return bpfObjName{}, errors.Errorf("invalid character '%c' in name '%s'", name[idx], name)
-	}
-
+func newBPFObjName(name string) bpfObjName {
 	var result bpfObjName
 	copy(result[:unix.BPF_OBJ_NAME_LEN-1], name)
-	return result, nil
+	return result
 }
 
 func invalidBPFObjNameChar(char rune) bool {
+	dotAllowed := objNameAllowsDot() == nil
+
 	switch {
 	case char >= 'A' && char <= 'Z':
 		fallthrough
@@ -36,6 +32,8 @@ func invalidBPFObjNameChar(char rune) bool {
 		fallthrough
 	case char >= '0' && char <= '9':
 		fallthrough
+	case dotAllowed && char == '.':
+		fallthrough
 	case char == '_':
 		return false
 	default:
@@ -345,10 +343,25 @@ func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) {
 }
 
 var haveObjName = internal.FeatureTest("object names", "4.15", func() bool {
-	name, err := newBPFObjName("feature_test")
+	attr := bpfMapCreateAttr{
+		mapType:    Array,
+		keySize:    4,
+		valueSize:  4,
+		maxEntries: 1,
+		mapName:    newBPFObjName("feature_test"),
+	}
+
+	fd, err := bpfMapCreate(&attr)
 	if err != nil {
-		// This really is a fatal error, but it should be caught
-		// by the unit tests not working.
+		return false
+	}
+
+	_ = fd.Close()
+	return true
+})
+
+var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() bool {
+	if err := haveObjName(); err != nil {
 		return false
 	}
 
@@ -357,7 +370,7 @@ var haveObjName = internal.FeatureTest("object names", "4.15", func() bool {
 		keySize:    4,
 		valueSize:  4,
 		maxEntries: 1,
-		mapName:    name,
+		mapName:    newBPFObjName(".test"),
 	}
 
 	fd, err := bpfMapCreate(&attr)


Test patch:
diff --git a/syscalls_test.go b/syscalls_test.go
index 3195319..a77e998 100644
--- a/syscalls_test.go
+++ b/syscalls_test.go
@@ -1,33 +1,46 @@
 package ebpf
 
 import (
+	"strings"
 	"testing"
 
 	"github.com/cilium/ebpf/internal/testutils"
+	"github.com/cilium/ebpf/internal/unix"
 )
 
-func TestObjName(t *testing.T) {
+func TestObjNameCharacters(t *testing.T) {
 	for in, valid := range map[string]bool{
-		"test":                         true,
-		"":                             true,
-		"a-b":                          false,
-		"yeah so":                      false,
-		"more_than_16_characters_long": true,
+		"test":    true,
+		"":        true,
+		"a-b":     false,
+		"yeah so": false,
+		"dot.":    objNameAllowsDot() == nil,
 	} {
-		name, err := newBPFObjName(in)
-		if result := err == nil; result != valid {
-			t.Errorf("Name '%s' classified incorrectly", name)
-		}
-		if name[len(name)-1] != 0 {
-			t.Errorf("Name '%s' is not null terminated", name)
+		result := strings.IndexFunc(in, invalidBPFObjNameChar) == -1
+		if result != valid {
+			t.Errorf("Name '%s' classified incorrectly", in)
 		}
 	}
 }
 
+func TestObjName(t *testing.T) {
+	name := newBPFObjName("more_than_16_characters_long")
+	if name[len(name)-1] != 0 {
+		t.Error("newBPFObjName doesn't null terminate")
+	}
+	if len(name) != unix.BPF_OBJ_NAME_LEN {
+		t.Errorf("Name is %d instead of %d bytes long", len(name), unix.BPF_OBJ_NAME_LEN)
+	}
+}
+
 func TestHaveObjName(t *testing.T) {
 	testutils.CheckFeatureTest(t, haveObjName)
 }
 
+func TestObjNameAllowsDot(t *testing.T) {
+	testutils.CheckFeatureTest(t, objNameAllowsDot)
+}
+
 func TestHaveNestedMaps(t *testing.T) {
 	testutils.CheckFeatureTest(t, haveNestedMaps)
 }